views:

226

answers:

5

The following line of code compiles just fine and behaves:

list<const int *> int_pointers;  // (1)

The following two lines do not:

typedef int * IntPtr;
list<const IntPtr> int_pointers;  // (2)

I get the exact same compile errors for

list<int * const> int_pointers;  // (3)

I'm well aware that the last line is not legal since the elements of an STL container need to be assignable. Why is the compiler interpreting (2) to be the same as (3) ?

+8  A: 

You read C-style type declarations right to left. So "const int *" is a pointer to constant ints ("const int" and "int const" mean the same thing). Those are perfectly assignable. But (2) and (3) are constant pointers to int, and therefore not assignable.

Pontus Gagge
+5  A: 

const IntPtr and const int* are not the same thing.

1) const int* is "pointer to const int".

2) const IntPtr expands to int * const (think (int *) const) which is "const pointer to int".

In short, the typedef is acting like a set of parentheses. You can't change the const-ness of what a typedef'd pointer points to.

Mike DeSimone
+3  A: 

const int * is the same as writing int const *, meaning a constant value pointed by a non constant pointer.

With the typedef you define the pointer itself as constant, like in your 3rd statement.

gregseth
+6  A: 

Short answer:

  1. is a list of pointers to constant ints.
  2. is a list of constant pointers to ints.
  3. is the same as 2.

const (and volatile) should naturally appear after the type they qualify. When you write it before, the compiler automatically rewrites it internally:

const int *

becomes

int const *

which is a pointer to a constant int. Lists of these will compile fine since the pointer itself is still assignable.

bltxd
+3  A: 

You are asking "Why is the compiler interpreting (2) to be the same as (3)?". Well, because in C++ language (as well as in C) they are semantically the same. When you define a typename as

typedef int *IntPtr;

then later the type const IntPtr will stand for int *const, not for const int *. That's just how typedef-names work in C++.

Typedef-names in C++ are not macros. While they do not define new types (just aliases for the existing ones), the resulting aliases are nevertheless "atomic", "monolithic" in a sense that any qualifiers applied to the alias will apply as top-level qualifiers. When you are working with a typedef-name, there's no way to "sneak in" a const qualifier so that it would somehow "descend" to a lower-level portion of the type (int in your case).

If you insist on using typedef-names, you have no other immediate choice but to provide two different typedef-names, like

typedef int *IntPtr;
typedef const int *ConstIntPtr;

and use ConstIntPtr when you need a pointer-to-const version of the type.

AndreyT
+1, good answer :-)
Prasoon Saurav