While trying to work with C++11 smart pointers and container classes, I've written two pieces of code which basically boil down to the following:
class Foo { ... };
typedef std::unique_ptr<Foo> FooPtr;
// First attempt; this did _not_ compile
typedef std::unordered_map<int, FooPtr> IntToFooMap;
// Second attempt; this _did_ compile
typedef std::unordered_map<int, const FooPtr &> IntToFooMap;
// here is the declaration causing the error in the second case:
IntToFooMap m {
{ 41, FooPtr(new Foo()) }
};
As you can see, first I tried to define an unordered map of FooPtr, which didn't compile, because FooPtr is an std::unique_ptr which is not copiable (it has a deleted copy constructor).
More precisely, it is the initialization of the map using an initializer list that caused the error.
To my great surprise, however, the second version, whereby I defined the value_type of the unordered map as a const reference to FooPtr, did compile.
Up until today, I thought that it is not possible in general to store values of reference type in standard containers. While this behavior is somewhat intuitively understandable, since declaring a (const) reference prevents the -- otherwise prohibited -- copying of the unique_ptr, I still don't fully understand how or why this works. If it works at all, of course; I would not be surprised if this was just a subtle corner case that resulted in undefined behavior.
You violated a constraint of the library. This results in undefined behavior. The library may or may not detect that it's a reference.
The problem with references is that the library is allowed to presume that the container owns the object which holds the value of the element, and therefore to call its destructor, in particular it can do this:
allocator_traits<A>::destroy(m, p) // m is the allocator, p points to object.
I didn't notice language explicitly requiring that the mapped type in a std::[unordered_]map be an object type, however you would have to take great care to avoid operator[] which would attempt to default-construct the value_type which is std::pair<int, FooPtr const &>. For the unordered container, CopyAssignability of value_type is also required, for the sake of using dynamic arrays as hash buckets, and although references are CopyAssignable (the copy devolves to the referenced object), a pair containing a reference is not.
I doubt that the intent of the library is to allow references there, but the prohibition may be missing. Anyway, I'm sleepy right now. Perhaps someone can find it.
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.
A const int * and an int *const are very different. Similarly with const std::auto_ptr<int> vs. std::auto_ptr<const int>. However, there appears to be no such distinction with const std::vector<int> vs. std::vector<const int> (actually I'm not sure the second is even allowed). Why is this?
Sometimes I have a function which I want to pass a reference to a vector. The function shouldn't modify the vector itself (eg. no push_back()), but it wants to modify each of the contained values (say, increment them). Similarly, I might want a function to only change the vector structure but not modify any of its existing contents (though this would be odd). This kind of thing is possible with std::auto_ptr (for example), but because std::vector::front() (for example) is defined as
const T &front() const;
T &front();
rather than just
T &front() const;
There's no way to express this.
Examples of what I want to do:
//create a (non-modifiable) auto_ptr containing a (modifiable) int
const std::auto_ptr<int> a(new int(3));
//this works and makes sense - changing the value pointed to, not the pointer itself
*a = 4;
//this is an error, as it should be
a.reset();
//create a (non-modifiable) vector containing a (modifiable) int
const std::vector<int> v(1, 3);
//this makes sense to me but doesn't work - trying to change the value in the vector, not the vector itself
v.front() = 4;
//this is an error, as it should be
v.clear();
It's a design decision.
If you have a const container, it usually stands to reason that you don't want anybody to modify the elements that it contains, which are an intrinsic part of it. That the container completely "owns" these elements "solidifies the bond", if you will.
This is in contrast to the historic, more lower-level "container" implementations (i.e. raw arrays) which are more hands-off. As you quite rightly say, there is a big difference between int const* and int * const. But standard containers simply choose to pass the constness on.
The difference is that pointers to int do not own the ints that they point to, whereas a vector<int> does own the contained ints. A vector<int> can be conceptualised as a struct with int members, where the number of members just happens to be variable.
If you want to create a function that can modify the values contained in the vector but not the vector itself then you should design the function to accept iterator arguments.
Example:
void setAllToOne(std::vector<int>::iterator begin, std::vector<int>::iterator end)
{
std::for_each(begin, end, [](int& elem) { elem = 1; });
}
If you can afford to put the desired functionality in a header, then it can be made generic as:
template<typename OutputIterator>
void setAllToOne(OutputIterator begin, OutputIterator end)
{
typedef typename iterator_traits<OutputIterator>::reference ref;
std::for_each(begin, end, [](ref elem) { elem = 1; });
}
One big problem syntactically with what you suggest is this: a std::vector<const T> is not the same type as a std::vector<T>. Therefore, you could not pass a vector<T> to a function that expects a vector<const T> without some kind of conversion. Not a simple cast, but the creation of a new vector<const T>. And that new one could not simply share data with the old; it would have to either copy or move the data from the old one to the new one.
You can get away with this with std::shared_ptr, but that's because those are shared pointers. You can have two objects that reference the same pointer, so the conversion from a std::shared_ptr<T> to shared_ptr<const T> doesn't hurt (beyond bumping the reference count). There is no such thing as a shared_vector.
std::unique_ptr works too because they can only be moved from, not copied. Therefore, only one of them will ever have the pointer.
So what you're asking for is simply not possible.
You are correct, it is not possible to have a vector of const int primarily because the elements will not assignable (requirements for the type of the element contained in the vector).
If you want a function that only modifies the elements of a vector but not add elements to the vector itself, this is primarily what STL does for you -- have functions that are agnostic about which container a sequence of elements is contained in. The function simply takes a pair of iterators and does its thing for that sequence, completely oblivious to the fact that they are contained in a vector.
Look up "insert iterators" for getting to know about how to insert something into a container without needing to know what the elements are. E.g., back_inserter takes a container and all that it cares for is to know that the container has a member function called "push_back".
I'm parsing a file and creating a vector of a vector of Foo objects.
vector< vector<Foo*> > mFooVectors;
I have to allow access with the following getFoos function.
const vector<const Foo*>& getFoos(int index);
So I created a const declaration of my vector:
const vector< const vector<const Foo*> > mConstFooVectors;
Now how do I do mConstFooVectors = mFooVectors so I can return a reference to mConstFooVectors?
You cannot add const at any given level that easy. Different instantiations of a template are different unrelated types, a vector<T> is unrelated to a vector<const T>, and there is no way of casting from one to the other.
You can, on the other hand, create a different vector and just copy the contents, but that might be expensive, as you would have to copy all the different contained vectors.
By the way, if you return a const reference to the outer vector, the const reference will behave as: const std::vector< const std::vector< Foo * const > >&, note that because of the value semantics associated to types in C++, const-ness propagates in. The problem is that the value stored in the inner vector is a pointer, and making that pointer constant does not make the pointed-to object constant. Similarly, the behavior of your getFoos(int) will be equivalent to const std::vector< Foo * const >&. Note, that is behavior not actual types.
I don't know what you are trying to do, but I'd have a look at boost::ptr_vector
Specially, your vector member can potentially leak if you don't handle it correctly... (RAII)
vector's copy constructor should be able to handle it, but you'll have to do it in the initializer list of your class's constructor since it's a const member.
Edit: That was wrong... Vector's copy constructor can't handle it. See David Rodriguez' post for an explanation (vector and vector are unrelated types).
You are mixing a
vector<const Foo *>
with
vector<Foo *>
You have to decide which one you want - there is no way to convert from one to another (without making a copy).
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).