Vector and Const

Vector of const objects giving compile error

Items in a vector must be assignable (or, in more recent versions of the standard, movable). const objects aren't assignable, so attempting to store them in a vector will fail (or at least can fail -- the code is invalid, but a compiler is free to accept it anyway, if it so chooses, though most programmers would generally prefer that invalid code be rejected).

I suppose for the truly pedantic, if you wanted to badly enough, you could define a type that was assignable despite being const, something like this:

class ugly { 
mutable int x;
public:
ugly const &operator=(ugly const &u) const {
x = u.x;
return *this;
}
};

I believe you should be able to store items of this type in a vector even though they're const. A quick test of creating a vector of these succeeds with VC++. This failed with some older compilers (e.g., failed with g++ 4.8.1), but works with reasonably recent ones (VC++ back to at least 2015, g++ back to at least 5.4 and clang++ back to at least 4.0--though I haven't tried to track down the first version of each that supported it).

For a current compiler, a type that supported moving const objects would probably work just as well. But, just in case it wasn't obvious: this is allowing you to modify an object even though it's marked const. That's clearly a direct violation of any reasonable user's expectations, so it's mostly a problem, not a solution.

const vector implies const elements?

The first version

v[0].set (1234); 

does not compile because it tries to change the vector's first element returned to it by reference. The compiler thinks it's a change because set(int) is not marked const.

The second version, on the other hand, only reads from the vector

(*v[0]).set(1234);

and calls set on the result of the dereference of a constant reference to a pointer that it gets back.

When you call v[0] on a const vector, you get back a const reference to A. When element type is a pointer, calling set on it is OK. You could change the second example to

v[0]->set(1234);

and get the same result as before. This is because you get a reference to a pointer that is constant, but the item pointed to by that pointer is not constant.

vector and const

I've added a few lines to your code. That's sufficient to make it clear why this is disallowed:

void f(vector<const T*>& p)
{
static const T ct;
p.push_back(&ct); // adds a const T* to nonConstVec !
}
int main()
{
vector<T*> nonConstVec;
f(nonConstVec);
nonConstVec.back()->nonConstFunction();
}

vector of const pointers?

The vector is probably the only container that requires the elements to be copy assignable. This is because the elements are guaranteed to be stored contiguously in memory. So if you exceed the capacity, a new chunk has to be allocated and elements re-assigned. You cannot do this with const elements.

Same error if you try std::vector<const int>, or in fact any const type.

What is the difference between const std::vectorT and std::vectorT const?

const applies to the thing to its left, except for when there is nothing on the left then it applies to the thing to its right.

So, const int a=1; and int const a=1; are equal.

const int *b and int const *b are equal (pointer to a constant int), but different to int * const b, which is a constant pointer to a non-constant int.

This applies to all data types, I choose int because it is easier to type than std::vector<T>.

Why can't I push_back to a vector of const elements?

According to this answer (with commentary from one of the C++11 designers), std::vector<const T> is not permitted by the Standard.

The answer suggests that it might be possible to supply a custom allocator which permits a vector with that allocator to hold const objects.

You're probably better off not attempting to do this.

Casting vectorint to const vectorconst int

You cannot cast a std::vector<int> to const std::vector<const int>.

Besides, it does not make sense to use a std::vector<const int> at all. It doesn't give you any more safety than a const std::vector<int>.

Not only that, C++ does not allow construction of std::vector<const T>. See Does C++11 allow vector<const T>? for more info.

Why is a vector of pointers not castable to a const vector of const pointers?

void fn(const vector<const char*>)

As the top-level const qualifier is dropped for the function type, this is (at the call site) equivalent to:

void fn(vector<const char*>)

Both of which request a copy of the passed vector, because Standard Library containers follow value semantics.

You can either:

  • call it via fn({vc.begin(), vc.end()}), requesting an explicit conversion
  • change the signature to, e.g. void fn(vector<const char*> const&), i.e. taking a reference

If you can modify the signature of fn, you can follow GManNickG's advice and use iterators / a range instead:

#include <iostream>
template<typename ConstRaIt>
void fn(ConstRaIt begin, ConstRaIt end)
{
for(; begin != end; ++begin)
{
std::cout << *begin << std::endl;
}
}

#include <vector>
int main()
{
char arr[] = "hello world";
std::vector<char *> vc;
for(char& c : arr) vc.push_back(&c);

fn(begin(vc), end(vc));
}

This gives the beautiful output


hello world
ello world
llo world
lo world
o world
world
world
orld
rld
ld
d

The fundamental issue is to pass around Standard Library containers. If you only need constant access to the data, you don't need to know the actual container type and can use the template instead. This removes the coupling of fn to the type of container the caller uses.

As you have noticed, it's a bad idea to allow access of a std::vector<T*> through a std::vector<const T*>&. But if you don't need to modify the container, you can use a range instead.

If the function fn shall not or cannot be a template, you could still pass around ranges of const char* instead of vectors of const char. This will work with any container that guarantees contiguous storage, such as raw arrays, std::arrays, std::vectors and std::strings.



Related Topics



Leave a reply



Submit