Firstly, technically the unary *
operator evaluates to an lvalue in this case. However, the term lvalue in C basically designates something that has location (address) in storage (memory). In C++ terminology even functions are lvalues. So, again, in your example above the unary *
yield an lvalue. You can take the address of that lvalue, if you wish to do so, i.e. you can evaluate &*i
, &i->first
and &i->second
(assuming the built-in unary &
).
Secondly, since you original example involves assignment, you must be actually talking about modifiable lvalues. You see, the property of being lvalue by itself has very little to do with being assignable. To use the built-in assignment operator, you need a modifiable lvalue. What you get from a dereferenced iterator is value_type
of std::map
. It is a pair with a const-qualified first member, as you know already. This automatically makes the first member non-modifiable, and this makes the entire pair non-modifiable by the built-in assignment operator. The second member of the pair is modifiable, as you observed yourself already.
So, once again, the dereference operator in this case returns an lvalue. This lvalue is non-modifiable as a whole, and its first member is non-modifiable as well. Its second member is a modifiable lvalue.
As for your assumption about how the elements of std::map
are stored, I would say that in a typical implementation they will be stored as pair<const K, V>
objects, i.e. exactly what the dereference operator evaluates to. Normally, the map does not need to modify the key portion of the pair after it is initialized, so it should not run into any problems with the fact that the first member of the pair is const-qualified.