views:

216

answers:

4

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 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)

Edit: In case it matters, I'm using gcc 4.3.2 to compile this.

A: 

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->.
Nikolai N Fetissov
In this case however, `T::pointer` is the same as `T::value_type*`, (i.e. `T::pointer` is an actual raw pointer) and so the requirement is fulfilled. – Charles Salvia
Charles Salvia
+3  A: 

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.

GMan
+11  A: 

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

AndreyT
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 Joyner
+2  A: 

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.

James McNellis
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 Salvia
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 McNellis