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)
);
Related
Given the following class based on containers of shared pointers,
class Foo;
class Bar {
public:
// ...
const std::vector<boost::shared_ptr<const Foo> >& getFoos() const { return foos_; }
private:
std::vector<boost::shared_ptr<Foo> > foos_;
};
which will not compile because
invalid initialization of reference of type ‘const std::vector<boost::shared_ptr<const Foo>, std::allocator<boost::shared_ptr<const Foo> > >&’ from expression of type ‘const std::vector<boost::shared_ptr<Foo>, std::allocator<boost::shared_ptr<Foo> > >’
The foos_ member needs to point to mutable Foo objects for internal use by the Bar object, but I don't want client code calling getFoos() to be able to modify anything.
Removing the const qualifier from Foo in the getFoos() return type fixes this. However, I understand that while std::vector propagates its constness to its elements, boost::shared_ptr does no such thing to the object it points to (naturally). Thus, it seems to me getFoos() no longer observes its const qualifier (even though the compiler doesn't complain), because client code can modify the Foo objects pointed to by the shared pointers returned.
Am I correct? If so, is there some way to write getFoos() so that it will return a const vector of const references to const objects without copying?
I could be wrong but I really don't think you can achieve this.
shared_ptr<Foo> can become a shared_ptr<const Foo> only through a construction of a new instance to shared_ptr<const Foo>
A reference to shared_ptr<Foo> cannot become a reference to shared_ptr<const Foo>, simply because they are two different types.
Here you are trying to get a reference to vector<shared_ptr<Foo>> into the form of const vector<shared_ptr<const Foo>>.
The first const is perfectly fine. Since it's okay to assign a reference to the same reference type with a const qualifier.
But the second const is not, as you are literally trying to convert a reference to a vector of Type A to a reference to a vector of Type B.
Instead of returning a std::vector<...> const&, what about returning a range? A range is a pair of iterators of some kind. In this case, your iterators will be to std::shared_ptr<const foo>. You can do this by writing up a quick iterator adapter that internally iterates over const_iterator to std::shared_ptr<foo>, but returns them as std::shared_ptr<const foo>.
Most operations you'll want to perform on a const vector can be performed on a range of random-access const_iterators.
You can return a const shared_ptr<Foo>* pointer instead of a vector, and use explicit ugly C-style casting to convert foos_.data() into whatever you want.
Since you're interested in returning a const vector, you don't lose much by returning a pointer, except sizing information, of course. You can always wrap the new pointer in a class designed to provide this sizing information, as supplied at moment of its creation.
Given the code in Visual Studio 2010:
void Foo::Bar() const
{
map_t::const_iterator iter(my_map_.find(key));
if(my_map_.end() != iter)
DoStuff(iter->second);
}
Do stuff will take the mapped_type by value. The mapped type is a copyable, movable, assignable type.
I get error messages that the key/value pair can't be copied when trying to access second. Even if I write on their own lines:
iter->second;
(*iter).second;
to ensure it's nothing to do with DoStuff...
I presume the iterator is making a copy of the key/value pair before returning operator-> or operator*.
How do I get a copy of the mapped type?
EDIT:
The map itself is of unsigned shorts to boost variants, roughly as such:
typedef struct{} empty_t;
typedef boost::variant<empty_t, double, long, some POD types> variant_t;
typedef std::map<unsigned short, variant_t> map_t;
And then as a private member of the class:
map_t my_map_;
And to be clear, the problem is not in passing to DoStuff. I can remove that line, and simply dereference the iterator and access second, and that will cause the compiler error.
if(my_map_.end() != iter)
iter->second; //Doesn't do anything, but illustrates the error.
EDIT
Answer invalid given question update as a result of boost::variant (will update with final answer once the rest of the problem information is available).
If DoStuff takes it's variable by reference or by value and you're passing the variable directly from a const_iterator then DoStuff must have a const reference or const value overloaded function call (unless the object type has a default const to non-const conversion available). Otherwise the compiler complains because your treating a const element as a non-const.
Another option is to assign iter->second to another variable (via constructor or assignment depending on the element type) and pass that into DoStuff -- but it's better to define a const overload version of the function.
I figured it out.
WAYYYYY away from this code, and a couple abstractions deep, the iterators to the map were being passed to std::partition! You can't partition a map, it seems.
I copied the contents of the map into a local vector and continued processing from there, and it cleared up all the error messages.
I have no idea why the compiler was getting so throughly confused. The problem code was somewhere completely unrelated. But it doesn't matter.
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)
);
I'm writing my own implementation of the C++ STL map container. Now I'm trying to implement the iterator. It should allow you to do something like iter->first and iter->second that returns the key/value respectively and where iter is an object not a pointer. I'm wondering how I should overload this? It's a bit confusing because I'm not sure what the return type should be; it has to be an object with members first/second I suppose. Is it typical to return a reference to a wrapper/interface object or something like that?
If you really mean the C++ standard library, then the value_type of a map is a pair. A pair has members first and second. Dereferencing an iterator in a map gives you a pair.
Yes, you'll need a proxy to hold the relevant reference.
As for the type: standard-library iterators typically dereference to something of type value_type. For map<K,V>, the value type is std::pair<K, V> (or rather, pair<key_type, mapped_type>), which is where you get the first/second interface from.
(One of Stephan Lavavej's lectures explains how the MSVC++ implementation uses the same underlying data structure for set and map; the only difference is that set::value_type equals set::key_type, while map::value_type is pair<key_type, mapped_type>. That way you can tell the two apart with a simple trait check, but the iterator interface is virtually identical.)
The value_type of a standard map is std::pair<const KeyType, MappedType>.
To achieve normal pointer semantics, operator* returns a reference, whereas operator-> returns a pointer.
//minimal example
#include <utility>
#include <cstdio>
struct It
{
std::pair<const int, int> pair;
std::pair<const int, int>* operator->() { return &pair; }
std::pair<const int, int>& operator*() { return pair; }
};
int main()
{
It it = {std::make_pair(10, 20) };
(*it).second = 30;
std::printf("%d %d\n", it->first, it->second);
}
std::map<K,V>::iterator iterates over objects of type std::pair<K,V>.
The answer to your question is yes. You should return a proxy-object or a reference to a proxy-object to get that behavior.
I have a member variable of type vector<T> (where is T is a custom class, but it could be int as well.)
I have a function from which I want to return a pointer to this vector, but I don't want the caller to be able to change the vector or it's items. So I want the return type to be const vector<const T>*
None of the casting methods I tried worked. The compiler keeps complaining that T is not compatible with const T.
Here's some code that demonstrates the gist of what I'm trying to do;
vector<int> a;
const vector<const int>* b = (const vector<const int>* ) (&a);
This code doesn't compile for me.
Thanks in advance!
If you have a const vector<int> you cannot modify the container, nor can you modify any of the elements in the container. You don't need a const vector<const int> to achieve those semantics.
On why a vector<T> cannot be correctly converted to a vector<const T> even if T can be converted to const T
This is a common recurring problem in programming whether it is with constness or inheritance (a container of derived object cannot be converted to a container of base objects, even if the contained elements themselves can). The problem is that element by element each one of them can be converted, but the container itself cannot without breaking the type system.
If you were allowed to do vector< const T > &vr = my_vector_of_T, then you would be allowed to add elements through vr, and those elements would be constant by definition. But at the same time those same elements would be aliased in my_vector_of_T as non-const elements and could be modified through that interface, breaking constness in the typesystem.
In the particular case of a vector<int> being converted to a vector<const int>, chances are that you would not notice really weird effects --besides adding an element to a vector<const int> and seeing how the constant element changes in time, but still remember that given two related types T1 and T2 for which a relation exists, in most cases trying to apply the same relationship to containers of T1 and T2 will break the type system.
In addition to James's answer about how to do it you should note that const int is not a valid type to put into any standard container since it is not assignable.
you can force conversion like this:
b = reinterpret_cast<const std::vector<const int>*>(&a);
but I do not think you should do this, since it is not guaranteed to work, only to compile
The compiler decides to block this. However, we know this is safe so maybe we can fool it:
const vector<const int>* b = (const vector<const int>* )(void *)(&a);