2

I am not sure why the following code is not allowed to exist:

int main() { std::vector<const int> v; v.reserve(2); v.emplace_back(100); v.emplace_back(200); } 

In theory, reserve() does not construct anything, as opposite to resize(). Also, emplace_back() is constructing "in place" objects, so none of this code is writing over an already constructed constant object.

Despite that, even writing the first line, std::vector<const int> v;, is already resulting in compilation error. Why is not allowed at all to have a std::vector of constants?

5
  • 3
    Some useful information here: stackoverflow.com/questions/6954906/… Commented Apr 6, 2020 at 12:13
  • 1
    emplace_back has to handle case of resize, even if not called. Commented Apr 6, 2020 at 12:13
  • 1
    @Jarod42 resize doesn't technically need to have problems with const though unless the type is move-only. Commented Apr 6, 2020 at 12:20
  • 1
    Or rather, reallocation doesn't. Commented Apr 6, 2020 at 12:26
  • Yes add another specialized definition to the standard... screams in std::vector<bool> Commented Apr 6, 2020 at 12:43

2 Answers 2

1

While the "why" is best read here there are some easy ways to get what you want:

template<class T> class as_const { T t; public: as_const(T& t_): t(t_) {} as_const(const as_const&) = default; as_const(as_const &&) = delete; as_const& operator=(const as_const&) = default; as_const& operator=(as_const&&) = delete; operator const T&() const { return t; } const T& operator*() const { // Or just a get method, anyway it's nicer to also have an explicit getter return t; } }; std::vector<as_const<int>> vec; vec.reserve(2) vec.emplace_back(100); vec.emplace_back(200); 

You even can decide "how constant" your wrapper should be and provide the move constructors (note that t is not constant per-se in as_const) if you think that is reasonable for your use-case.

Note that you cannot prohibit the reallocation of your vector in this way. If you want to do that (and don't want to use a compile-time size array since you now your size only at runtime) take a look at std::unique_ptr<T[]>. But note that in this case you first have to create a mutable variant and then reseat it in a const variant, since the underlying array gets default initialized and you cannot change anything after that.

Of course there is also the possibility of working with an allocator and disallowing a reallocation. But that has no stl-implementation. There are some implementations for this type of behavior out there (I myself gave it a try once but that is a bit of a mess) but I do not know if there is anything in boost.

Sign up to request clarification or add additional context in comments.

Comments

1

As the link provided by Bathsheba tells you, the problem is that std::vector<T> is really std::vector<T, std::allocator<T>>. Up to C++20, std::allocator<T>::address() had two overloads, one which takes T& and one which takes T const&. The problem with T=const int should be obvious. C++ has no such thing as extra-const, so both overloads are equally good.

2 Comments

The problem is not the return type but the argument it takes (reference vs const-reference which collapses if T is const). The return type of a function is not considered for the signature in overload resolution, so if the two address() overloads would only differ in the return type, we had a problem regardless whether T is constant or nor.
@n314159: Fixed.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.