C++ : struggle with generic const pointer
I've run into some annoying issues with const-correctness in some templated code, that ultimately boils down to the following observation: for some reason, given an STL-ish Container type T, const typename T::pointer
does not actually seem to yeild a constant pointer type, even if T::pointer
is equivalent to T::value_type*
.
The following example illustrates the problem. Suppose you have a templated function that takes a Container which must meet the STL Random Access Container concept requirements.
template <class Container>
void example(Container& c)
{
const typename Container::pointer p1 = &c[0]; // Error if c is const
const typename Container::value_type* p2 = &c[0];
}
Then, if we pass this function a const container...
const std::vector<int> vec(10);
example(vec);
...we get an invalid conversion from const int*
to int*
. But why is const typename Contai开发者_运维技巧ner::pointer
not the same as const int*
in this example?
Note that if I change const typename Container::pointer
to simply typename Container::const_pointer
it compiles fine, however, as far as I can tell, the const_pointer typedef is an extension, (I don't see it mentioned in the C++ standard Container Requirements (23.5, Table 65)), and so therefore I don't want to use it.
So how can I obtain a generic, const-correct pointer type from a container T? (I really can't see how to do this without using boost::mpl::if_ along with type_traits to check if the container is constant...but there must be a less verbose way to do this)
Edit: In case it matters, I'm using gcc 4.3.2 to compile this.
It doesn't work because your const
does not apply to what you think it applies to. For example, if you have
typedef int* IntPtr;
then
const IntPtr p;
does not stand for
const int* p;
but rather stands for
int* const p;
Typedef-name is not a macro. Once the "pointerness" of the type is wrapped into a typedef-name, there's no way to use it to create a pointer-to-const type anymore. I.e. there's absolutely no way to use the above IntPtr
typedef-name to produce an equivalent of
const int* p;
You have to either use to pointee type explicitly (as you did with value_type
), or check whether your container defines a different typedef-name, with const
already wrapped "inside" (like const_pointer
or something like that).
This:
typename Container::pointer
Has the type int*
(in our case). I don't know the terminology, so sorry for that, but pointers point to a type. That is, Container::pointer
is a pointer to a mutable T, and adding const is only going to make this a const pointer (not a pointer to const), because Container::pointer has already been defined to point to a mutable T.
It seems only const_pointer
, either from the class or your own:
typedef const typename Container::value_type* const_pointer
Will work.
The allocator requirements (cf. 20.1.5, Table 32, "Allocator requirements") state that allocators for the STL containers shall have an Allocator::const_pointer
. All of the STL containers (the eight containers listed in Section 23) define a Container::const_pointer
typedef as:
typedef typename Allocator::const_pointer const_pointer;
As you state in your question, however, containers (other than those eight) are not required to have a Container::const_pointer
typedef.
This might be of relevance here (from SGI STL reference):
[6] As in the case of references [5], the pointer type must have the same semantics as C++ pointers but need not actually be a C++ pointer. "Smart pointers," however, unlike "smart references", are possible. This is because it is possible for user-defined types to define the dereference operator and the pointer member access operator, operator* and operator->.
It is possible by using a template. The trick is to remove the pointer first, then apply const on the type and finally applying pointer to the type - for instance using std::remove_pointer
yields:
typedef const std::remove_pointer<Container::pointer>::type* PointerToConst;
精彩评论