11

While this compiles:

char* p2c;
const char* p2cc = p2c; //fine

because lhs pointed type has all the qualifiers of rhs pointed type, this does not:

char** p2p2c;
const char** p2p2cc = p2p2c; //fail

but this does:

const char * const * p2cp2cc = p2p2c; //fine

Why exactly does this happen?

emesx
  • 12,555
  • 10
  • 58
  • 91
  • 1
    The last example will only compile in C++, but not in C. – AnT stands with Russia Sep 05 '13 at 14:45
  • That's just the way it is in C and C++. These are two different languages with different const-correctness rules. In C++ `T **` is convertible to `const T* const *`. In C `T **` is convertible to `T* const *`, but not to `T* const *`. http://stackoverflow.com/a/5249001/187690 – AnT stands with Russia Sep 05 '13 at 17:20
  • @AndreyT your example lists same type in opposite meanings ;d (`T* const *`) – emesx Sep 05 '13 at 17:37
  • 1
    Oh... A copy-paste error. I meant to say "In C `T **` is convertible to `T* const *`, but not to `const T* const *`. – AnT stands with Russia Sep 05 '13 at 17:58
  • 6
    I'll quote a part of your answer that I find particularly concise: *C++ says that you can add const-qualification at any depth of indirection, as long as you also add const-qualification all the way to the top level. In C you can only add const-qualification to the type pointed by the top-level pointer, but no deeper.* – emesx Sep 06 '13 at 06:13

2 Answers2

7

This does not work:

char** p2p2c;
const char** p2p2cc = p2p2c; //fail

If that was allowed you would be allowed to break const-correctness:

const int k = 10;
int *p;
int **pp = &p;
int const **kpp = pp;     // Should this be allowed, if so:
*kpp = &k;                // fine, kpp promises not to change it
                          // yet this does p = &k;
                          // p made no such promise! this is a hidden const_cast!
*p = 5;

If the assignment was allowed, you would enable setting a non-const pointer (intermediate) to refer to a constant value, possibly causing undefined behavior in a non-obvious to see way. By disallowing that operation the type system is safer.

but this does:

const char * const * p2cp2cc = p2p2c; //fine

This is fine, since the intermediate pointer is fixed, it is impossible to reset the intermediate pointer to refer to a const object and break const-correctness

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
-1

cdecl really helps in cases like this.

const char** p2p2cc = declare p2p2cc as pointer to pointer to const char

and

const char * const * p2cp2cc = declare p2cp2cc as pointer to const pointer to const char

As you can see, the second version has the inner AND the outer pointer const, meaning it cannot modify either. The first version has the INNER pointer const and the outer non-const thereby breaking constnes.

On the other hand, this works:

char** const p = p2p2c;
user1233963
  • 1,450
  • 15
  • 41