Related
So I have a smart iterator that emulates a map const_iterator, and it needs to build the return type internally. Obviously, I'd like to store a pair<Key, Value> in my iterator class (since I need to modify it), but at the same time I'd like the dereference functions to present a pair<const Key, Value> (actually it would be a const pair<const Key, Value>& and const pair<const Key, Value>* respectively). The only solution I've come up with so far is to dynamically allocate a new pair every time change the value that my iterator class points to changes. Needless to say, this is not a good solution.
I've also tried *const_cast<const pair<const Key, Value> >(&value) where value is declared as pair<Key, Value>.
Any help would be greatly appreciated (as would the knowledge that it can't be done).
EDIT
For the curious: I ended up storing a pair<const Key, Value> p in my iterator class. In order to change the pair I alter the two elements separately based on the underlying iterator (map<Key, Value>::const_iterator it), const_casting the key so that it could be altered, like this:
*const_cast<Key*>(&p.first) = it->first;
p.second = it->second;
Not a solution I'm terribly happy with, but it gets the job done, and the dereference methods are happy because I'm storing something of the correct type, which they can refer to.
You can convert a value of type pair<Key,Value> to pair<const Key,Value>.
However, reading the question carefully, you're actually asking if, given a pair<Key,Value> you can create a pointer or reference to pair<const Key,Value> referring to the same object.
The answer is no - the only situation where a reference or pointer to one type can refer to an object of a different type is if the object type inherits from the referenced type.
One possibility is to return a pair of references, pair<const Key&, Value&>, created from the pair you wish to reference.
Yes.
std::pair<int, double> p(1,2);
std::pair<const int, double> q = p; // no problem
//q.first = 8; // error
q.second = 9;
int b; double d;
std::pair<int &, double &> s(b,d);
std::pair<int const &, double &> t = s; // also fine
As Kerrek SB pointed out, you can construct std::pair<const Key, Value> from std::pair<Key, Value>. However, your original question implies that you want to avoid constructing std::pair objects each time your iterator is dereferenced.
Unfortunately, there is not a good way to do this. You may have to construct the pair object and actually store it somewhere, particular for operator->. Otherwise you have to be able to have your map actually store pair<const Key, Value> to be able to return references/pointers to it from your iterator. Basically to return a reference/pointer, it has to be stored somewhere in that form: it cannot be a temporary.
Avoid the const_cast. That's just asking for undefined behavior when you use it to cast pair this way even though it may work quite often.
I met exactly the same problem. My solution was to create a new map object as part of the iterator class, then add to it members that are missed in the upstream map class and return the reference to members of it. Not very efficient, but works.
Your solution have two issues:
Assigning a const variable with const_cast is undefined behavior. The compiler optimisations could provide strange results.
Any new dereference would invalidate the results of previous dereference. It should not to. So, depending of the usage of your iterator it may also produce strange results.
Corrected by #antonpp, this is an undefined behaviour that shouldn't be recommended any more.
This can be solved by reinterpret_cast, and it is purely a compile-time directive.
std::pair<int, double> p1 = { 1, 2.3 };
auto& p2 = reinterpret_cast<std::pair<const int, double>&>(p1); // type of p2 is pair<const int, double>&
//++(p2.first); // error: expression must be a modifiable lvalue
++(p1.first);
assert(p2.first == 2);
However, reinterpret_cast is a dangerous behaviour. I would suggest adding an static assert, to prevent unexpected partial template specialization.
using srcType = std::pair<Key, Value>;
using tarType = std::pair<const Key, Value>;
static_assert(
offsetof(srcType, first) == offsetof(tarType, first)
&& offsetof(srcType, second) == offsetof(tarType, second)
);
So I have a smart iterator that emulates a map const_iterator, and it needs to build the return type internally. Obviously, I'd like to store a pair<Key, Value> in my iterator class (since I need to modify it), but at the same time I'd like the dereference functions to present a pair<const Key, Value> (actually it would be a const pair<const Key, Value>& and const pair<const Key, Value>* respectively). The only solution I've come up with so far is to dynamically allocate a new pair every time change the value that my iterator class points to changes. Needless to say, this is not a good solution.
I've also tried *const_cast<const pair<const Key, Value> >(&value) where value is declared as pair<Key, Value>.
Any help would be greatly appreciated (as would the knowledge that it can't be done).
EDIT
For the curious: I ended up storing a pair<const Key, Value> p in my iterator class. In order to change the pair I alter the two elements separately based on the underlying iterator (map<Key, Value>::const_iterator it), const_casting the key so that it could be altered, like this:
*const_cast<Key*>(&p.first) = it->first;
p.second = it->second;
Not a solution I'm terribly happy with, but it gets the job done, and the dereference methods are happy because I'm storing something of the correct type, which they can refer to.
You can convert a value of type pair<Key,Value> to pair<const Key,Value>.
However, reading the question carefully, you're actually asking if, given a pair<Key,Value> you can create a pointer or reference to pair<const Key,Value> referring to the same object.
The answer is no - the only situation where a reference or pointer to one type can refer to an object of a different type is if the object type inherits from the referenced type.
One possibility is to return a pair of references, pair<const Key&, Value&>, created from the pair you wish to reference.
Yes.
std::pair<int, double> p(1,2);
std::pair<const int, double> q = p; // no problem
//q.first = 8; // error
q.second = 9;
int b; double d;
std::pair<int &, double &> s(b,d);
std::pair<int const &, double &> t = s; // also fine
As Kerrek SB pointed out, you can construct std::pair<const Key, Value> from std::pair<Key, Value>. However, your original question implies that you want to avoid constructing std::pair objects each time your iterator is dereferenced.
Unfortunately, there is not a good way to do this. You may have to construct the pair object and actually store it somewhere, particular for operator->. Otherwise you have to be able to have your map actually store pair<const Key, Value> to be able to return references/pointers to it from your iterator. Basically to return a reference/pointer, it has to be stored somewhere in that form: it cannot be a temporary.
Avoid the const_cast. That's just asking for undefined behavior when you use it to cast pair this way even though it may work quite often.
I met exactly the same problem. My solution was to create a new map object as part of the iterator class, then add to it members that are missed in the upstream map class and return the reference to members of it. Not very efficient, but works.
Your solution have two issues:
Assigning a const variable with const_cast is undefined behavior. The compiler optimisations could provide strange results.
Any new dereference would invalidate the results of previous dereference. It should not to. So, depending of the usage of your iterator it may also produce strange results.
Corrected by #antonpp, this is an undefined behaviour that shouldn't be recommended any more.
This can be solved by reinterpret_cast, and it is purely a compile-time directive.
std::pair<int, double> p1 = { 1, 2.3 };
auto& p2 = reinterpret_cast<std::pair<const int, double>&>(p1); // type of p2 is pair<const int, double>&
//++(p2.first); // error: expression must be a modifiable lvalue
++(p1.first);
assert(p2.first == 2);
However, reinterpret_cast is a dangerous behaviour. I would suggest adding an static assert, to prevent unexpected partial template specialization.
using srcType = std::pair<Key, Value>;
using tarType = std::pair<const Key, Value>;
static_assert(
offsetof(srcType, first) == offsetof(tarType, first)
&& offsetof(srcType, second) == offsetof(tarType, second)
);
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).