Is it only my compiler or is it forbidden to use cons references in std::vector elements. Consider following structure:
struct Y
{
const int & x;
Y(const int & p_x):
x(p_x)
{
}
};
Now, when I try to push such object onto vector:
std::vector<Y> yv;
int x = 5;
Y y(x);
yv.push_back(y);
I get compiler error: "error: non-static reference member `const int&Y::x', can't use default assignment operator". Shouldn't copy ctor be enough?
The vector elements must be assignable. From section 23.2.4 Class template vector of the C++ standard:
...the stored object shall meet the requirements of Assignable.
You may want to check
std::reference_wrapper
available with C++11
No, because you can't assign to a const reference and the STL containers make use of assignment for moving items around.
You're better using a shared_ptr (boost:: or std:: depending on your environment) or a raw pointer.
An assignment operator is necessary because vector default constructs elements in extra slots ahead of time.
Later, instead of reallocating, it uses the assignment operator to assign those default instances to whatever you push into the vector.
It is generally forbidden to use references in any container. You can justify this by the fact, that a vector for example allocates the data in forehand and references can just be assigned once (during initialization).
I finally used
std::vector< const Type* >
Not the best looking, but it does the same job.
Related
The standard C++ containers offer only one version of operator[] for containers like vector<T> and deque<T>. It returns a T& (other than for vector<bool>, which I'm going to ignore), which is an lvalue. That means that in code like this,
vector<BigObject> makeVector(); // factory function
auto copyOfObject = makeVector()[0]; // copy BigObject
copyOfObject will be copy constructed. Given that makeVector() returns an rvalue vector, it seems reasonable to expect copyOfObject to be move constructed.
If operator[] for such containers was overloaded for rvalue and lvalue objects, then operator[] for rvalue containers could return an rvalue reference, i.e., an rvalue:
template<typename T>
container {
public:
T& operator[](int index) &; // for lvalue objects
T&& operator[](int index) &&; // for rvalue objects
...
};
In that case, copyOfObject would be move constructed.
Is there a reason this kind of overloading would be a bad idea in general? Is there a reason why it's not done for the standard containers in C++14?
Converting comment into answer:
There's nothing inherently wrong with this approach; class member access follows a similar rule (E1.E2 is an xvalue if E1 is an rvalue and E2 names a non-static data member and is not a reference, see [expr.ref]/4.2), and elements inside a container are logically similar to non-static data members.
A significant problem with doing it for std::vector or other standard containers is that it will likely break some legacy code. Consider:
void foo(int &);
std::vector<int> bar();
foo(bar()[0]);
That last line will stop compiling if operator[] on an rvalue vector returned an xvalue. Alternatively - and arguably worse - if there is a foo(const int &) overload, it will silently start calling that function instead.
Also, returning a bunch of elements in a container and only using one element is already rather inefficient. It's arguable that code that does this probably doesn't care much about speed anyway, and so the small performance improvement is not worth introducing a potentially breaking change.
I think you will leave the container in an invalid state if you move out one of the elements, I would argue the need to allow that state at all. Second, if you ever need that, can't you just call the new object's move constructor like this:
T copyObj = std::move(makeVector()[0]);
Update:
Most important point is, again in my opinion, that containers are containers by their nature, so they should not anyhow modify the elements inside them. They just provide a storage, iteration mechanism, etc.
If I have a type like this:
class Foo {
public:
Foo();
Foo(const Foo&);
Foo& operator=(const Foo&) = delete;
...
private:
...
};
And I have two vectors of that type:
std::vector<Foo> x;
std::vector<Foo> y;
And I want to copy the contents of x to y, is there a cross-platform way of doing so?
VC++ will do this with y.assign(x.begin(), x.end()), which uses Foo's copy constructor instead of the deleted copy assignment operator. But GCC complains about the missing copy assignment operator, whether you try y = x or y.assign(x.begin(), x.end()).
Is there some way that will work in both?
The standard requires that for y.assign(it1, it2) to work, the element type T be CopyAssignable. That is, copy constructible and assignable. Your type is not assignable, so you can't rely on assign. The same goes for y = x. Section 23.2.3 of the standard describes the requirements for various sequence container operations.
If you have an existing vector y, you can construct a new vector z then swap it with y:
{
std::vector<Foo> z(x.begin(), x.end());
z.swap(y);
}
This uses the range constructor, which requires only that T be EmplaceConstructible, no assignment operator needed! You can then swap the underlying memory without any further copies.
Note that this may result in larger memory usage, since any existing content of y will persist until the freshly swapped z goes out of scope. You could try to mitigate this by first doing
y.clear();
y.shrink_to_fit();
although shrink_to_fit() is only a request, and may not be honoured by your library implementation.
[Live demo]
Prior to C++11 your code would not have been possible: the vector element type had to be CopyAssignable, meaning copy-constructible and assignable. Note that if the type does not meet these requirements, the code is ill-formed with no diagnostic required; this gives compilers latitude to either accept or reject it.
Since C++11, individual operations have their own requirements. The relevant requirements for vector are (source: C++14 Table 100):
emplace_back : MoveInsertible and MoveAssignable
insert: CopyInsertable and CopyAssignable
constructor: EmplaceConstructible
The meanings of these traits are (roughly):
CopyInsertable: has copy-constructor
MoveInsertable: has copy-constructor or move-constructor (or both)
CopyAssignable: has copy-constructor and copy-assignment operator
MoveAssignable: MoveInsertable, and has copy-assignment or move-assignment operator (or both)
EmplaceConstructible: has constructor
The rationale here is that any insertion may cause a memory reallocation, which requires that your objects be able to be either copied or moved. However the Standard does not require the compiler to test whether constructor or assignment operator are each available, and use one if the other isn't. Instead it specifies that both must be available.
You didn't specify whether your class is movable. Assuming it isn't, then that means you cannot use any of the emplace or insert methods, nor any other function that might cause reallocation.
If you are creating y at the time then you can of course use initialization:
vector<int> y = x;
If y already exists, as your question seems to suggest, then you're nearly out of luck: you can't use any insertion functions, so you can only modify the existing elements. And since there is no assignment operator, you can't do this via assignment.
But you can use y.swap() as suggested in Andrew's answer.
Consider adding move-constructor and move-assignment to your class, or redesigning your code so that you do not need to assign to y. (E.g. use a pointer).
If x.size() == y.size() then you can use placement new as a last-resort hack:
for (size_t i = 0; i != y.size(); ++i)
{
y[i].~Foo();
new(&y[i]) Foo(x[i]); // copy-construct
}
The standard C++ containers offer only one version of operator[] for containers like vector<T> and deque<T>. It returns a T& (other than for vector<bool>, which I'm going to ignore), which is an lvalue. That means that in code like this,
vector<BigObject> makeVector(); // factory function
auto copyOfObject = makeVector()[0]; // copy BigObject
copyOfObject will be copy constructed. Given that makeVector() returns an rvalue vector, it seems reasonable to expect copyOfObject to be move constructed.
If operator[] for such containers was overloaded for rvalue and lvalue objects, then operator[] for rvalue containers could return an rvalue reference, i.e., an rvalue:
template<typename T>
container {
public:
T& operator[](int index) &; // for lvalue objects
T&& operator[](int index) &&; // for rvalue objects
...
};
In that case, copyOfObject would be move constructed.
Is there a reason this kind of overloading would be a bad idea in general? Is there a reason why it's not done for the standard containers in C++14?
Converting comment into answer:
There's nothing inherently wrong with this approach; class member access follows a similar rule (E1.E2 is an xvalue if E1 is an rvalue and E2 names a non-static data member and is not a reference, see [expr.ref]/4.2), and elements inside a container are logically similar to non-static data members.
A significant problem with doing it for std::vector or other standard containers is that it will likely break some legacy code. Consider:
void foo(int &);
std::vector<int> bar();
foo(bar()[0]);
That last line will stop compiling if operator[] on an rvalue vector returned an xvalue. Alternatively - and arguably worse - if there is a foo(const int &) overload, it will silently start calling that function instead.
Also, returning a bunch of elements in a container and only using one element is already rather inefficient. It's arguable that code that does this probably doesn't care much about speed anyway, and so the small performance improvement is not worth introducing a potentially breaking change.
I think you will leave the container in an invalid state if you move out one of the elements, I would argue the need to allow that state at all. Second, if you ever need that, can't you just call the new object's move constructor like this:
T copyObj = std::move(makeVector()[0]);
Update:
Most important point is, again in my opinion, that containers are containers by their nature, so they should not anyhow modify the elements inside them. They just provide a storage, iteration mechanism, etc.
My comments on this answer got me thinking about the issues of constness and sorting. I played around a bit and reduced my issues to the fact that this code:
#include <vector>
int main() {
std::vector <const int> v;
}
will not compile - you can't create a vector of const ints. Obviously, I should have known this (and intellectually I did), but I've never needed to create such a thing before. However, it seems like a useful construct to me, and I wonder if there is any way round this problem - I want to add things to a vector (or whatever), but they should not be changed once added.
There's probably some embarrassingly simple solution to this, but it's something I'd never considered before.
I probably should not have mentioned sorting (I may ask another question about that, see this for the difficulties of asking questions). My real base use case is something like this:
vector <const int> v; // ok (i.e. I want it to be OK)
v.push_back( 42 ); // ok
int n = v[0]; // ok
v[0] = 1; // not allowed
Well, in C++0x you can...
In C++03, there is a paragraph 23.1[lib.containers.requirements]/3, which says
The type of objects stored in these components must meet the requirements of CopyConstructible types (20.1.3), and the additional requirements of Assignable types.
This is what's currently preventing you from using const int as a type argument to std::vector.
However, in C++0x, this paragraph is missing, instead, T is required to be Destructible and additional requirements on T are specified per-expression, e.g. v = u on std::vector is only valid if T is MoveConstructible and MoveAssignable.
If I interpret those requirements correctly, it should be possible to instantiate std::vector<const int>, you'll just be missing some of its functionality (which I guess is what you wanted). You can fill it by passing a pair of iterators to the constructor. I think emplace_back() should work as well, though I failed to find explicit requirements on T for it.
You still won't be able to sort the vector in-place though.
Types that you put in a standard container have to be copyable and assignable. The reason that auto_ptr causes so much trouble is precisely because it doesn't follow normal copy and assignment semantics. Naturally, anything that's const is not going to be assignable. So, you can't stick const anything in a standard container. And if the element isn't const, then you are going to be able to change it.
The closest solution that I believe is possible would be to use an indirection of some kind. So, you could have a pointer to const or you could have an object which holds the value that you want but the value can't be changed within the object (like you'd get with Integer in Java).
Having the element at a particular index be unchangeable goes against how the standard containers work. You might be able to construct your own which work that way, but the standard ones don't. And none which are based on arrays will work regardless unless you can manage to fit their initialization into the {a, b, c} initialization syntax since once an array of const has been created, you can't change it. So, a vector class isn't likely to work with const elements no matter what you do.
Having const in a container without some sort of indirection just doesn't work very well. You're basically asking to make the entire container const - which you could do if you copy to it from an already initialized container, but you can't really have a container - certainly not a standard container - which contains constants without some sort of indirection.
EDIT: If what you're looking to do is to mostly leave a container unchanged but still be able to change it in certain places in the code, then using a const ref in most places and then giving the code that needs to be able to change the container direct access or a non-const ref would make that possible.
So, use const vector<int>& in most places, and then either vector<int>& where you need to change the container, or give that portion of the code direct access to the container. That way, it's mostly unchangeable, but you can change it when you want to.
On the other hand, if you want to be able to pretty much always be able to change what's in the container but not change specific elements, then I'd suggest putting a wrapper class around the container. In the case of vector, wrap it and make the subscript operator return a const ref instead of a non-const ref - either that or a copy. So, assuming that you created a templatized version, your subscript operator would look something like this:
const T& operator[](size_t i) const
{
return _container[i];
}
That way, you can update the container itself, but you can't change it's individual elements. And as long as you declare all of the functions inline, it shouldn't be much of a performance hit (if any at all) to have the wrapper.
You can't create a vector of const ints, and it'd be pretty useless even if you could. If i remove the second int, then everything from there on is shifted down one -- read: modified -- making it impossible to guarantee that v[5] has the same value on two different occasions.
Add to that, a const can't be assigned to after it's declared, short of casting away the constness. And if you wanna do that, why are you using const in the first place?
You're going to need to write your own class. You could certainly use std::vector as your internal implementation. Then just implement the const interface and those few non-const functions you need.
Although this doesn't meet all of your requirements (being able to sort), try a constant vector:
int values[] = {1, 3, 5, 2, 4, 6};
const std::vector<int> IDs(values, values + sizeof(values));
Although, you may want to use a std::list. With the list, the values don't need to change, only the links to them. Sorting is accomplished by changing the order of the links.
You may have to expend some brain power and write your own. :-(
I would have all my const objects in a standard array.
Then use a vector of pointers into the array.
A small utility class just to help you not have to de-reference the objects and hay presto.
#include <vector>
#include <algorithm>
#include <iterator>
#include <iostream>
class XPointer
{
public:
XPointer(int const& data)
: m_data(&data)
{}
operator int const&() const
{
return *m_data;
}
private:
int const* m_data;
};
int const data[] = { 15, 17, 22, 100, 3, 4};
std::vector<XPointer> sorted(data,data+6);
int main()
{
std::sort(sorted.begin(), sorted.end());
std::copy(sorted.begin(), sorted.end(), std::ostream_iterator<int>(std::cout, ", "));
int x = sorted[1];
}
I'm with Noah: wrap the vector with a class that exposes only what you want to allow.
If you don't need to dynamically add objects to the vector, consider std::tr1::array.
If constness is important to you in this instance I think you probably want to work with immutable types all the way up. Conceptually you'll have a fixed size, const array of const ints. Any time you need to change it (e.g. to add or remove elements, or to sort) you'll need to make a copy of the array with the operation performed and use that instead.
While this is very natural in a functional language it doesn't seem quite "right" in C++. getting efficient implementations of sort, for example, could be tricky - but you don't say what you're performance requirements are.
Whether you consider this route as being worth it from a performance/ custom code perspective or not I believe it is the correct approach.
After that holding the values by non-const pointer/ smart pointer is probably the best (but has its own overhead, of course).
I've been thinking a bit on this issue and it seems that you requirement is off.
You don't want to add immutable values to your vector:
std::vector<const int> vec = /**/;
std::vector<const int>::const_iterator first = vec.begin();
std::sort(vec.begin(), vec.end());
assert(*vec.begin() == *first); // false, even though `const int`
What you really want is your vector to hold a constant collection of values, in a modifiable order, which cannot be expressed by the std::vector<const int> syntax even if it worked.
I am afraid that it's an extremely specified task that would require a dedicated class.
It is true that Assignable is one of the standard requirements for vector element type and const int is not assignable. However, I would expect that in a well-thought-through implementation the compilation should fail only if the code explicitly relies on assignment. For std::vector that would be insert and erase, for example.
In reality, in many implementations the compilation fails even if you are not using these methods. For example, Comeau fails to compile the plain std::vector<const int> a; because the corresponding specialization of std::allocator fails to compile. It reports no immediate problems with std::vector itself.
I believe it is a valid problem. The library-provided implementation std::allocator is supposed to fail if the type parameter is const-qualified. (I wonder if it is possible to make a custom implementation of std::allocator to force the whole thing to compile.) (It would also be interesting to know how VS manages to compile it) Again, with Comeau std::vector<const int> fails to compiler for the very same reasons std::allocator<const int> fails to compile, and, according to the specification of std::allocator it must fail to compile.
Of course, in any case any implementation has the right to fail to compile std::vector<const int> since it is allowed to fail by the language specification.
Using just an unspecialized vector, this can't be done. Sorting is done by using assignment. So the same code that makes this possible:
sort(v.begin(), v.end());
...also makes this possible:
v[1] = 123;
You could derive a class const_vector from std::vector that overloads any method that returns a reference, and make it return a const reference instead. To do your sort, downcast back to std::vector.
std::vector of constant object will probably fail to compile due to Assignable requirement, as constant object can not be assigned. The same is true for Move Assignment also. This is also the problem I frequently face when working with a vector based map such as boost flat_map or Loki AssocVector. As it has internal implementation std::vector<std::pair<const Key,Value> > .
Thus it is almost impossible to follow const key requirement of map, which can be easily implemented for any node based map.
However it can be looked, whether std::vector<const T> means the vector should store a const T typed object, or it merely needs to return a non-mutable interface while accessing.
In that case, an implementation of std::vector<const T> is possible which follows Assignable/Move Assignable requirement as it stores object of type T rather than const T. The standard typedefs and allocator type need to be modified little to support standard requirements.Though to support such for a vector_map or flat_map, one probably needs considerable change in std::pair interface as it exposes the member variables first & second directly.
Compilation fails because push_back() (for instance) is basically
underlying_array[size()] = passed_value;
where both operand are T&. If T is const X that can't work.
Having const elements seem right in principle but in practice it's unnatural, and the specifications don't say it should be supported, so it's not there. At least not in the stdlib (because then, it would be in vector).
I am not sure why this doesn't compile:
std::vector< const Obj& > myVector;
void foo(const Obj& obj)
{
myVector.push_back( obj );
}
Sorry, a bit of additional info on what I'm trying to achieve: I can't change the signature of foo without breaking an interface, but I just want to hang onto the Objects that get passed through foo. I do want to store pointers to them, but I'm confused about the syntax of how to do that.
You can't have an vector of references. as the things in a vector must be copyable and assignable, and references are neither of these. You probably want a vector of pointers:
std::vector< const Obj * > myVector;
void foo( const Obj & obj )
{
myVector.push_back( & obj );
}
I'm not sure if you can put references into a container like this. Use
std::vector<const Obj *> myVector
instead as that's semantically equivalent anyway.
You cannot use references as the types in a vector. You can use raw pointers, smart pointers (boost/std::tr1 shared_ptr) or other constructs like boost::ref and boost::cref that provide wrappers around plain references to adapt to containers.
Vectors only work with value types; you cannot have a vector of references.
Part of the reason is that references must be bound to an existing variable as they are created, and they can never be re-bound. For a vector to be able to hold 10 elements at their "default" value is impossible with a reference type:
std::vector<const T& obj>(10); // cannot work
Similarly, you cannot re-assign a value to a reference (unless your intention was to modify the value of the original variable) so suddenly myVec[0] = 7 doesn't work either.
Alternatively you can use boost::ref to store references, but it is much like storing pointers.
std::vector< boost::ref< Obj > > myVector;
As per your edits, if you want to store pointers, you can just write something like:
std::vector < const Obj* > myVector;
void foo(const Obj& obj)
{
myVector.push_back(&obj);
}
If you use boost::ref or boost::cref:
std::vector < boost::cref < Obj > > myVector;
void foo(const Obj& obj)
{
myVector.push_back(boost::cref< Obj >(obj) );
}
(probably you can ommit the last boost::cref in the body too, but I don't have now a compiler at hand).
The C++ Standard in chapater 23.1 Container requirements specifies:
3 The type of objects stored in these
components must meet the requirements
of CopyConstructible types (20.1.3),
and the additional requirements of
Assignable types.
In other words, a type T is usable with standard C++ containers as value type as long as it defines copy constructor. The CopyConstructible concept is explain by the standard in the cited chapater but Boost manual explains clearly and shortly what means a type is CopyConstructible
Reference does not fulfil this requirement.
You cannot store references in an STL container because references are not copy constructable. TR1 contains a reference wrapper if you really need to "simulate" storing references. Storing smart pointers (e.g. tr1::shared_ptr) or object instances is usually better.
Aside from problems with references being the target type for a container, you can't have const objects as the target type for a container because the type of objects that a container is instantiated with must be "Assignable" as well as "CopyConstructable" (23.1 Container requirements). Const objects aren't assignable.
To be assignable, after a 't = u operation, t must be equivalent to u where t is of type T - the type the container was instantiated with.
However, as mentioned in other answers, you can put pointers to const objects in containers.