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::array
s, std::vector
s and std::string
s.
Related Topics
Undefined Symbols for Architecture X86_64: Which Architecture Should I Use
Unit Test That a Class Is Non Copyable, and Other Compile-Time Properties
Constexpr Not Working If the Function Is Declared Inside Class Scope
Why C++ Doesn't Support Named Parameter
How Are Objects Stored in Memory in C++
Extract C++ Template Parameters
Create Shared_Ptr to Stack Object
Code Runs 6 Times Slower with 2 Threads Than with 1
How to Detect Text Area from Image
Where Does the -Dndebug Normally Come From
Threadsafe Vector Class for C++
What Does Exactly the Warning Mean About Hidden Symbol Being Referenced by Dso
How to Write Video File in Opencv 2.4.3
Qmetaobject::Connectslotsbyname: No Matching Signal
Are Non Dereferenced Iterators Past the "One Past-The-End" Iterator of an Array Undefined Behavior