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.

...we get an invalid conversion from const int* to int*. But why is const typename Container::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)

4 Answers
4

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 can be difficult to remember. The best hint I've come across is to read R to L. "int* const p" is "const pointer to int", i.e. pointer is const but int is mutable. "const int *p" is "pointer to int that is const", i.e. pointer is mutable but int is const.
–
David JoynerOct 30 '09 at 1:10

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.

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.

Can you clarify a bit on this? I see what you're saying regarding const_pointer as part of the Allocator requirement (in 20.1.5), but where does it say that Containers must necessarily define a const_pointer typedef? The closest I see is 20.1.5.4, where it says Container implementations can assume that an Allocator has a const_pointer typedef, not necessarily that the container itself should define a public const_pointer typedef.
–
Charles SalviaOct 30 '09 at 0:49

I didn't mean to imply that the STL requires containers to define a const_pointer typedef, just that all of the STL containers do define a const_pointer typedef. Sorry for the confusion; I updated my answer to try and make it a little clearer.
–
James McNellisOct 30 '09 at 0:55

[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->.