The problem:
In C++ arrays and SequenceContainers http://en.cppreference.com/w/cpp/concept/SequenceContainer (std::array
, std::deque
, std::vector
) are necessarily zero-indexed. However, from the semantical point of view, elements of an array may be assigned a completely other range of indexes. One common example is the need to store a 1-indexed array in a 0-indexed container. (I remember having to operate on an arbitrarily-indexed arrays, however)
Solutions to the above problems include:
- remembering to skew the index by a given constant each time such an array is indexed – this leads to lots of code like
vec[n-1]
or sth like that – but this approach is tedious and error-prone (it’s easy to forget this-1
and instead typevec[n]
, oops); - Defining a class for such an array that would have a method like
some_type &operator [] (unsigned n) {return m_arr[n-1];}
– but this can often be quite overkillish; - The crude approach: Forget about the fact that arrays are 0-indexed; treat them as if they were 1-indexed and never use index 0. This approach is, however, well – crude, and it will fail with arbitrarily-indexed arrays.
The proposed solution:
I thought I’d write a wrapper for SequenceContainers that would differ from standard containers only in that its operator[]
and the at
functions would skew their arguments by a given offset. I wrote two versions of this wrapper: one, when the offset is determined compile-time as a template argument, and the other one, when it can be modified run-time. I believe that both are needed.
The usage is simple, as shown in a trivial example of calculating lengths of Collatz sequence for the first 100 initial values: ( https://en.wikipedia.org/wiki/Collatz_conjecture ) (sorry for the naive algorithm)
#include "shiftsc.hpp"
using namespace std;
using element = unsigned;
using length = unsigned;
length compute_length(element n)
{return n == 1 ? 1 : (1 + compute_length(n % 2 == 0 ? n / 2 : n * 3 + 1));}
int main()
{
constexpr element max_element = 100;
// one-indexed std::array<length, max_element>
shifted_SequenceContainer<array<length, max_element>, 1> Collatz_lengths;
// or for short: shifted_array<length, max_element, 1> Collatz_lengths;
for(element i = 1; i <= max_element; i++)
Collatz_lengths[i] = compute_length(i);
}
Seen working: http://ideone.com/Xy8A2U
(I’ll provide a use-case for the variation with run-time offset later, in a separate question – I have an idea for yet another wrapper that would need this functionality to operate)
The problems with the solution:
Before I present You the code, I have to write down a few issues I can see with this code I don’t know how to fix... (inb4 close votes: they don’t make the code broken!)
- The length of the code. Ikr, it is hilarious. There’s very little I can do, however: writing containers requires defining a TON of different methods, and although the code of most of them is trivial, their sheer amount is what makes this code 453 lines long. I thought about different ‘solutions" to this issue, none of which worked: • defining a base class for container wrappers – impossible b/c of the incompatibility of
virtual
with templates (couldn’t allow overloading methods liketemplate <class Iterator> void assign (Iterator it, Iterator jt)
; • defining repetitive methods and the whole class in terms of macros; this did work and did shorten the code, but IMO made it utterly unreadable, so I gave up. std::array
can be initialized with a braced-init-list; unfortunately,shifted_SequenceContainer<std::array</*...*/>/*...*/>
cannot. And I don’t really have many ideas how to fix this. https://stackoverflow.com/questions/40599052/how-to-write-a-wrapper-for-stdarrays-list-initialization-constructor https://stackoverflow.com/questions/40599829/how-to-pass-a-braced-init-list-to-stdarray-s-constructor- The naming. Ikr,
shifted_SequenceContainer
breaks all naming conventions... But what can I do? The concept is calledSequenceContainer
, so if I changed that, I’d cease referring to this concept... It’s calledSequenceContainer
notsequence_container
so a name likeshifted_sequence_container
won’t fit, and a name likeShiftedSequenceContainer
would be incongruent with the naming of standard containers – after all,std::unordered_map
is calledunordered_map
and notUnorderedMap
.
The code:
Enough of the (lengthy) preface, here comes the actual code:
#ifndef GAAZKAM_SHIFTSC_H
#define GAAZKAM_SHIFTSC_H
#include <utility>
#include <initializer_list>
#include <stdexcept>
#include <cstddef>
#include <array>
#include <vector>
#include <deque>
// Known problems: syntax like shifted_array<int, 4, 7> arr({1,2,3,4}) wont work
// With static offset
// Container must satisfy SequenceContainer
template<class Container, typename Container::difference_type Offset>
class shifted_SequenceContainer
{
public:
using value_type = typename Container::value_type;
using reference = typename Container::reference;
using const_reference = typename Container::const_reference;
using pointer = typename Container::pointer;
using const_pointer = typename Container::const_pointer;
using iterator = typename Container::iterator;
using const_iterator = typename Container::const_iterator;
using reverse_iterator = typename Container::reverse_iterator;
using const_reverse_iterator = typename Container::const_reverse_iterator;
using difference_type = typename Container::difference_type;
using size_type = typename Container::size_type;
using container_type = Container;
// Must be public; otherwise the user mayn’t access e.g. vector::reserve,
// which might be needed for optimization
Container cont;
static constexpr typename Container::difference_type offset = Offset;
explicit shifted_SequenceContainer() {}
shifted_SequenceContainer(shifted_SequenceContainer const &that)
: cont(that.cont) {}
shifted_SequenceContainer(shifted_SequenceContainer &&that)
: cont(std::move(that.cont)) {}
explicit shifted_SequenceContainer(Container const &that) : cont(that) {}
explicit shifted_SequenceContainer(Container &&that) : cont(that) {}
shifted_SequenceContainer &operator = (shifted_SequenceContainer const &that)
{cont = that.cont; return *this;}
shifted_SequenceContainer &operator = (shifted_SequenceContainer &&that)
{cont = std::move(that.cont); return *this;}
shifted_SequenceContainer &operator = (Container const &that)
{cont = that; return *this;}
shifted_SequenceContainer &operator = (Container &&that)
{cont = std::move(that); return *this;}
shifted_SequenceContainer
(size_type n, value_type const &val = value_type())
: cont(n, val) {}
template<class Iterator> shifted_SequenceContainer(Iterator it, Iterator jt)
: cont(it, jt) {}
shifted_SequenceContainer(std::initializer_list<value_type> init)
: cont(init) {}
shifted_SequenceContainer &operator = (std::initializer_list<value_type> init)
{cont = init; return *this;}
void assign(size_type n, value_type const &val) {return cont.assign(n, val);}
template<class Iterator> void assign(Iterator it, Iterator jt)
{return cont.assign(it, jt);}
void assign(std::initializer_list<value_type> init)
{return cont.assign(init);}
void swap(shifted_SequenceContainer &that) {return cont.swap(that.cont);}
friend void swap
(shifted_SequenceContainer &lhs, shifted_SequenceContainer &rhs)
{return swap(lhs.cont, rhs.cont);}
friend bool operator ==
(shifted_SequenceContainer const &lhs, shifted_SequenceContainer const &rhs)
{return lhs.cont == rhs.cont;}
friend bool operator !=
(shifted_SequenceContainer const &lhs, shifted_SequenceContainer const &rhs)
{return lhs.cont != rhs.cont;}
friend bool operator <
(shifted_SequenceContainer const &lhs, shifted_SequenceContainer const &rhs)
{return lhs.cont < rhs.cont;}
friend bool operator <=
(shifted_SequenceContainer const &lhs, shifted_SequenceContainer const &rhs)
{return lhs.cont <= rhs.cont;}
friend bool operator >
(shifted_SequenceContainer const &lhs, shifted_SequenceContainer const &rhs)
{return lhs.cont > rhs.cont;}
friend bool operator >=
(shifted_SequenceContainer const &lhs, shifted_SequenceContainer const &rhs)
{return lhs.cont >= rhs.cont;}
constexpr size_type size() const {return cont.size();}
constexpr size_type max_size() const {return cont.max_size();}
void resize(size_type n) {return cont.resize(n);}
void resize(size_type n, value_type const &val) {return cont.resize(n, val);}
constexpr bool empty() const {return cont.empty();}
template<class ...Args> iterator emplace(const_iterator pos, Args &&...args)
{return cont.emplace(pos, std::move(args...));}
template<class ...Args> void emplace_front(Args &&...args)
{return cont.emplace_front(std::move(args...));}
template<class ...Args> void emplace_back(Args &&...args)
{return cont.emplace_back(std::move(args...));}
iterator insert(const_iterator pos, value_type const &val)
{return cont.insert(pos, val);}
iterator insert(const_iterator pos, value_type &&val)
{return cont.insert(pos, std::move(val));}
iterator insert(const_iterator pos, size_type n, value_type const &val)
{return cont.insert(pos, n, val);}
template<class Iterator> void insert(iterator pos, Iterator it, Iterator jt)
{return cont.insert(pos, it, jt);}
iterator insert(const_iterator pos, std::initializer_list<value_type> init)
{return cont.insert(pos, init);}
void push_front(value_type const &val) {return cont.push_front(val);}
void push_front(value_type &&val) {return cont.push_front(std::move(val));}
void push_back(value_type const &val) {return cont.push_back(val);}
void push_back(value_type &&val) {return cont.push_back(std::move(val));}
void clear() {return cont.clear();}
iterator erase(const_iterator pos) {return cont.erase(pos);}
iterator erase(const_iterator it, const_iterator jt)
{return cont.erase(it, jt);}
void pop_front() {return cont.pop_front();}
void pop_back() {return cont.pop_back();}
iterator begin() {return cont.begin();}
constexpr const_iterator begin() const {return cont.begin();}
constexpr const_iterator cbegin() const {return cont.cbegin();}
iterator end() {return cont.end();}
constexpr const_iterator end() const {return cont.end();}
constexpr const_iterator cend() const {return cont.cend();}
reverse_iterator rbegin() {return cont.rbegin();}
constexpr const_reverse_iterator rbegin() const {return cont.rbegin();}
constexpr const_reverse_iterator crbegin() const {return cont.crbegin();}
reverse_iterator rend() {return cont.rend();}
constexpr const_reverse_iterator rend() const {return cont.rend();}
constexpr const_reverse_iterator crend() const {return cont.crend();}
reference front() {return cont.front();}
constexpr const_reference front() const {return cont.front();}
reference back() {return cont.back();}
constexpr const_reference back() const {return cont.back();}
// Here things actually start to change
reference operator[] (difference_type pos)
{return cont.operator[](pos-offset);}
constexpr const_reference operator[] (difference_type pos) const
{return cont.operator[](pos-offset);}
reference at (difference_type pos)
{if(pos-offset<0) throw std::out_of_range(""); return cont.at(pos-offset);}
constexpr const_reference at (difference_type pos) const
{if(pos-offset<0) throw std::out_of_range(""); return cont.at(pos-offset);}
// Additionally let’s provide these ones:
iterator find(difference_type pos)
{return cont.begin()+(pos-offset);}
constexpr const_iterator find(difference_type pos) const
{return cont.cbegin()+(pos-offset);}
};
template
<class T, std::size_t N, typename std::array<T, N>::difference_type offset>
using shifted_array = shifted_SequenceContainer<std::array<T, N>, offset>;
template<class T, typename std::vector<T>::difference_type offset>
using shifted_vector = shifted_SequenceContainer<std::vector<T>, offset>;
template<class T, typename std::deque<T>::difference_type offset>
using shifted_deque = shifted_SequenceContainer<std::deque<T>, offset>;
// With dynamic offset
// Container must satisfy SequenceContainer
template<class Container>
class dynshifted_SequenceContainer
{
public:
using value_type = typename Container::value_type;
using reference = typename Container::reference;
using const_reference = typename Container::const_reference;
using pointer = typename Container::pointer;
using const_pointer = typename Container::const_pointer;
using iterator = typename Container::iterator;
using const_iterator = typename Container::const_iterator;
using reverse_iterator = typename Container::reverse_iterator;
using const_reverse_iterator = typename Container::const_reverse_iterator;
using difference_type = typename Container::difference_type;
using size_type = typename Container::size_type;
using container_type = Container;
// Must be public; otherwise the user mayn’t access e.g. vector::reserve,
// which might be needed for optimization
Container cont;
difference_type offset = 0;
explicit dynshifted_SequenceContainer() {}
dynshifted_SequenceContainer(dynshifted_SequenceContainer const &that)
: cont(that.cont), offset(that.offset) {}
template<typename Container::difference_type StaticOffset>
dynshifted_SequenceContainer
(shifted_SequenceContainer<Container, StaticOffset> const &that)
: cont(that.cont), offset(StaticOffset) {}
dynshifted_SequenceContainer(dynshifted_SequenceContainer &&that)
: cont(std::move(that.cont)), offset(that.offset) {}
template<typename Container::difference_type StaticOffset>
dynshifted_SequenceContainer
(shifted_SequenceContainer<Container, StaticOffset> &&that)
: cont(std::move(that.cont)), offset(StaticOffset) {}
explicit dynshifted_SequenceContainer
(Container const &that, difference_type off = difference_type{})
: cont(that), offset(off) {}
explicit dynshifted_SequenceContainer
(Container &&that, difference_type off = difference_type{})
: cont(that), offset(off) {}
dynshifted_SequenceContainer &operator =
(dynshifted_SequenceContainer const &that)
{offset = that.offset; cont = that.cont; return *this;}
template<typename Container::difference_type StaticOffset>
dynshifted_SequenceContainer &operator =
(shifted_SequenceContainer<Container, StaticOffset> const &that)
{cont = that.cont; offset = StaticOffset; return *this;}
dynshifted_SequenceContainer &operator = (dynshifted_SequenceContainer &&that)
{offset = that.offset; cont = std::move(that.cont); return *this;}
template<typename Container::difference_type StaticOffset>
dynshifted_SequenceContainer &operator =
(shifted_SequenceContainer<Container, StaticOffset> &&that)
{{cont = std::move(that.cont); offset = StaticOffset; return *this;}}
dynshifted_SequenceContainer &operator = (Container const &that)
{cont = that; return *this;}
dynshifted_SequenceContainer &operator = (Container &&that)
{cont = std::move(that); return *this;}
explicit dynshifted_SequenceContainer
(
size_type n,
value_type const &val = value_type(),
difference_type offset = 0
)
: cont(n, val), offset(offset) {}
explicit dynshifted_SequenceContainer(size_type n, difference_type offset)
: cont(n, value_type{}), offset(offset) {}
template<class Iterator> dynshifted_SequenceContainer
(Iterator it, Iterator jt, difference_type offset = 0)
: cont(it, jt), offset(offset) {}
dynshifted_SequenceContainer
(std::initializer_list<value_type> init, difference_type offset = 0)
: cont(init) {}
dynshifted_SequenceContainer &operator =
(std::initializer_list<value_type> init)
{cont = init; return *this;}
void assign(size_type n, value_type const &val) {return cont.assign(n, val);}
void assign(size_type n, value_type const &val, difference_type offset)
{this->offset = offset; return cont.assign(n, val);}
template<class Iterator> void assign(Iterator it, Iterator jt)
{return cont.assign(it, jt);}
template<class Iterator>
void assign(Iterator it, Iterator jt, difference_type offset)
{this.offset = offset; return cont.assign(it, jt);}
void assign(std::initializer_list<value_type> init)
{return cont.assign(init);}
void assign(std::initializer_list<value_type> init, difference_type offset)
{this.offset = offset; return cont.assign(init);}
void swap(dynshifted_SequenceContainer &that)
{std::swap(this->offset, that.offset); return cont.swap(that.cont);}
friend void swap
(dynshifted_SequenceContainer &lhs, dynshifted_SequenceContainer &rhs)
{std::swap(lhs.offset, rhs.offset); return swap(lhs.cont, rhs.cont);}
// I provide all those overloads despite the implicit conversion for
// optimisation: implicit conversion is always linear, while those
// overloads may sometimes evaluate in constant
friend bool operator ==
(
dynshifted_SequenceContainer const &lhs,
dynshifted_SequenceContainer const &rhs
)
{return lhs.cont == rhs.cont;}
template<typename Container::difference_type StaticOffset>
friend bool operator ==
(
shifted_SequenceContainer<Container, StaticOffset> const &lhs,
dynshifted_SequenceContainer const &rhs
)
{return StaticOffset == rhs.offset && lhs.cont == rhs.cont;}
template<typename Container::difference_type StaticOffset>
friend bool operator ==
(
dynshifted_SequenceContainer const &lhs,
shifted_SequenceContainer<Container, StaticOffset> const &rhs
)
{return lhs.offset == StaticOffset && lhs.cont == rhs.cont;}
friend bool operator !=
(
dynshifted_SequenceContainer const &lhs,
dynshifted_SequenceContainer const &rhs
)
{return lhs.cont != rhs.cont;}
template<typename Container::difference_type StaticOffset>
friend bool operator !=
(
shifted_SequenceContainer<Container, StaticOffset> const &lhs,
dynshifted_SequenceContainer const &rhs
)
{return StaticOffset != rhs.offset || lhs.cont != rhs.cont;}
template<typename Container::difference_type StaticOffset>
friend bool operator !=
(
dynshifted_SequenceContainer const &lhs,
shifted_SequenceContainer<Container, StaticOffset> const &rhs
)
{return lhs.offset != StaticOffset || lhs.cont != rhs.cont;}
friend bool operator <
(
dynshifted_SequenceContainer const &lhs,
dynshifted_SequenceContainer const &rhs
)
{return lhs.cont < rhs.cont;}
template<typename Container::difference_type StaticOffset>
friend bool operator <
(
shifted_SequenceContainer<Container, StaticOffset> const &lhs,
dynshifted_SequenceContainer const &rhs
)
{return StaticOffset < rhs.offset || lhs.cont < rhs.cont;}
template<typename Container::difference_type StaticOffset>
friend bool operator <
(
dynshifted_SequenceContainer const &lhs,
shifted_SequenceContainer<Container, StaticOffset> const &rhs
)
{return lhs.offset < StaticOffset || lhs.cont < rhs.cont;}
friend bool operator <=
(
dynshifted_SequenceContainer const &lhs,
dynshifted_SequenceContainer const &rhs
)
{return lhs.cont <= rhs.cont;}
template<typename Container::difference_type StaticOffset>
friend bool operator <=
(
shifted_SequenceContainer<Container, StaticOffset> const &lhs,
dynshifted_SequenceContainer const &rhs
)
{return StaticOffset <= rhs.offset || lhs.cont <= rhs.cont;}
template<typename Container::difference_type StaticOffset>
friend bool operator <=
(
dynshifted_SequenceContainer const &lhs,
shifted_SequenceContainer<Container, StaticOffset> const &rhs
)
{return lhs.offset <= StaticOffset || lhs.cont <= rhs.cont;}
friend bool operator >
(
dynshifted_SequenceContainer const &lhs,
dynshifted_SequenceContainer const &rhs
)
{return lhs.cont > rhs.cont;}
template<typename Container::difference_type StaticOffset>
friend bool operator >
(
shifted_SequenceContainer<Container, StaticOffset> const &lhs,
dynshifted_SequenceContainer const &rhs
)
{return StaticOffset > rhs.offset || lhs.cont > rhs.cont;}
template<typename Container::difference_type StaticOffset>
friend bool operator >
(
dynshifted_SequenceContainer const &lhs,
shifted_SequenceContainer<Container, StaticOffset> const &rhs
)
{return lhs.offset > StaticOffset || lhs.cont > rhs.cont;}
friend bool operator >=
(
dynshifted_SequenceContainer const &lhs,
dynshifted_SequenceContainer const &rhs
)
{return lhs.cont >= rhs.cont;}
template<typename Container::difference_type StaticOffset>
friend bool operator >=
(
shifted_SequenceContainer<Container, StaticOffset> const &lhs,
dynshifted_SequenceContainer const &rhs
)
{return StaticOffset >= rhs.offset || lhs.cont >= rhs.cont;}
template<typename Container::difference_type StaticOffset>
friend bool operator >=
(
dynshifted_SequenceContainer const &lhs,
shifted_SequenceContainer<Container, StaticOffset> const &rhs
)
{return lhs.offset >= StaticOffset || lhs.cont >= rhs.cont;}
constexpr size_type size() const {return cont.size();}
constexpr size_type max_size() const {return cont.max_size();}
void resize(size_type n) {return cont.resize(n);}
void resize(size_type n, value_type const &val) {return cont.resize(n, val);}
constexpr bool empty() const {return cont.empty();}
template<class ...Args> iterator emplace(const_iterator pos, Args &&...args)
{return cont.emplace(pos, std::move(args...));}
template<class ...Args> void emplace_front(Args &&...args)
{return cont.emplace_front(std::move(args...));}
template<class ...Args> void emplace_back(Args &&...args)
{return cont.emplace_back(std::move(args...));}
iterator insert(const_iterator pos, value_type const &val)
{return cont.insert(pos, val);}
iterator insert(const_iterator pos, value_type &&val)
{return cont.insert(pos, std::move(val));}
iterator insert(const_iterator pos, size_type n, value_type const &val)
{return cont.insert(pos, n, val);}
template<class Iterator> void insert(iterator pos, Iterator it, Iterator jt)
{return cont.insert(pos, it, jt);}
iterator insert(const_iterator pos, std::initializer_list<value_type> init)
{return cont.insert(pos, init);}
void push_front(value_type const &val) {return cont.push_front(val);}
void push_front(value_type &&val) {return cont.push_front(std::move(val));}
void push_back(value_type const &val) {return cont.push_back(val);}
void push_back(value_type &&val) {return cont.push_back(std::move(val));}
void clear() {return cont.clear();}
iterator erase(const_iterator pos) {return cont.erase(pos);}
iterator erase(const_iterator it, const_iterator jt)
{return cont.erase(it, jt);}
void pop_front() {return cont.pop_front();}
void pop_back() {return cont.pop_back();}
iterator begin() {return cont.begin();}
constexpr const_iterator begin() const {return cont.begin();}
constexpr const_iterator cbegin() const {return cont.cbegin();}
iterator end() {return cont.end();}
constexpr const_iterator end() const {return cont.end();}
constexpr const_iterator cend() const {return cont.cend();}
reverse_iterator rbegin() {return cont.rbegin();}
constexpr const_reverse_iterator rbegin() const {return cont.rbegin();}
constexpr const_reverse_iterator crbegin() const {return cont.crbegin();}
reverse_iterator rend() {return cont.rend();}
constexpr const_reverse_iterator rend() const {return cont.rend();}
constexpr const_reverse_iterator crend() const {return cont.crend();}
reference front() {return cont.front();}
constexpr const_reference front() const {return cont.front();}
reference back() {return cont.back();}
constexpr const_reference back() const {return cont.back();}
reference operator[] (difference_type pos)
{return cont.operator[](pos-offset);}
constexpr const_reference operator[] (difference_type pos) const
{return cont.operator[](pos-offset);}
reference at (difference_type pos)
{if(pos-offset<0) throw std::out_of_range(""); return cont.at(pos-offset);}
constexpr const_reference at (difference_type pos) const
{if(pos-offset<0) throw std::out_of_range(""); return cont.at(pos-offset);}
iterator find(difference_type pos)
{return cont.begin()+(pos-offset);}
constexpr const_iterator find(difference_type pos) const
{return cont.cbegin()+(pos-offset);}
};
template<class T, std::size_t N>
using dynshifted_array = dynshifted_SequenceContainer<std::array<T, N>>;
template<class T>
using dynshifted_vector = dynshifted_SequenceContainer<std::vector<T>>;
template<class T>
using dynshifted_deque = dynshifted_SequenceContainer<std::deque<T>>;
#endif
I’m doing this hobbystically, and I’m quite much of a greenhorn with programming, so I’d highly appreciate any comments. Thanks in advance!
-
\$\begingroup\$ To quote Stanford algorithm's expert Donald Knuth, "Who are you? How did you get in my house?" \$\endgroup\$I'll add comments tomorrow– I'll add comments tomorrow2016年11月19日 18:47:53 +00:00Commented Nov 19, 2016 at 18:47
1 Answer 1
I believe that you are expending way too much energy trying to solve something that should be a non-problem. Simply always let your containers be zero based, if you have for example 1-based indexes as input, simply subtract 1 from them as soon as you get the input and convert back right before output. Always use zero-based indexes internally and only convert as close to input/output as possible.
(too large for comment really)
-
\$\begingroup\$ If this was for a one-time use, then yes, I agree: this would be an extreme overkill. But, it’s a one-time write. I expend so much energy to write this this time – and I have a more convenient container for the rest of my life. Is it such a bad idea? \$\endgroup\$gaazkam– gaazkam2016年11月18日 13:34:43 +00:00Commented Nov 18, 2016 at 13:34
-
\$\begingroup\$ Plus, this (alongside with
asInt
) is just one of quite a lot ideas I have to include in a purely hobbystically written and completely non-production "library" of wrappers and data structures to ease solvinguva.onlinejudge.org
tasks... The idea comes from the fact how I noticed much of my code in these tasks was repetitive and not very neat... so I thought I’d write a bunch of helpers to neaten these codes \$\endgroup\$gaazkam– gaazkam2016年11月18日 13:38:45 +00:00Commented Nov 18, 2016 at 13:38 -
1\$\begingroup\$ You are of course free to do as you please, but in my 10+ years of solving online judge type problems and in my life as a professional software engineer, I have never had use for such a container. In fact including such code would take more time than to adjust the values when I read them in. Because
#include <whatever.hpp
is simply more text than-1
once when I read in the value. \$\endgroup\$Emily L.– Emily L.2016年11月19日 12:17:26 +00:00Commented Nov 19, 2016 at 12:17