why `int**` can't be casted to `const int**` [duplicate] - c++

It is legal to convert a pointer-to-non-const to a pointer-to-const.
Then why isn't it legal to convert a pointer to pointer to non-const to a pointer to pointer to const?
E.g., why is the following code illegal:
char *s1 = 0;
const char *s2 = s1; // OK...
char *a[MAX]; // aka char **
const char **ps = a; // error!

From the standard:
const char c = 'c';
char* pc;
const char** pcc = &pc; // not allowed
*pcc = &c;
*pc = 'C'; // would allow to modify a const object

Ignoring your code and answering the principle of your question, see this entry from the comp.lang.c FAQ:
Why can't I pass a char ** to a function which expects a const char **?
The reason that you cannot assign a char ** value to a const char ** pointer is somewhat obscure. Given that the const qualifier exists at all, the compiler would like to help you keep your promises not to modify const values. That's why you can assign a char * to a const char *, but not the other way around: it's clearly safe to "add" const-ness to a simple pointer, but it would be dangerous to take it away. However, suppose you performed the following more complicated series of assignments:
const char c = 'x'; /* 1 */
char *p1; /* 2 */
const char **p2 = &p1; /* 3 */
*p2 = &c; /* 4 */
*p1 = 'X'; /* 5 */
In line 3, we assign a char ** to a const char **. (The compiler should complain.) In line 4, we assign a const char * to a const char *; this is clearly legal. In line 5, we modify what a char * points to--this is supposed to be legal. However, p1 ends up pointing to c, which is const. This came about in line 4, because *p2 was really p1. This was set up in line 3, which is an assignment of a form that is disallowed, and this is exactly why line 3 is disallowed.
And as your question is tagged C++ and not C, it even explains what const qualifiers to use instead:
(C++ has more complicated rules for assigning const-qualified pointers which let you make more kinds of assignments without incurring warnings, but still protect against inadvertent attempts to modify const values. C++ would still not allow assigning a char ** to a const char **, but it would let you get away with assigning a char ** to a const char * const *.)

Just since nobody has posted the solution, here:
char *s1 = 0;
const char *s2 = s1; // OK...
char *a[MAX]; // aka char **
const char * const*ps = a; // no error!
(http://www.parashift.com/c++-faq-lite/const-correctness.html#faq-18.17 for why)

The C++11 draft standard explains this in a note in section 4.4 which says:
[ Note: if a program could assign a pointer of type T** to a pointer
of type const T** (that is, if line #1 below were allowed), a program
could inadvertently modify a const object (as it is done on line #2).
For example,
int main() {
const char c = 'c';
char* pc;
const char** pcc = &pc; // #1: not allowed
*pcc = &c;
*pc = 'C'; // #2: modifies a const object
}
—end note ]
An interesting related question is Given int **p1 and const int **p2 is p1 == p2 well formed?.
Note the C++ FAQ also has an explanation for this but I like the explanation from the standard better.
The conforming text that goes with the note is as follows:
A conversion can add cv-qualifiers at levels other than the first in
multi-level pointers, subject to the following rules:56
Two pointer types T1 and T2 are similar if there exists a type T and
integer n > 0 such that:
T1 is cv1,0 pointer to cv1,1 pointer to · · · cv1,n−1 pointer to cv1,n
T
and
T2 is cv2,0 pointer to cv2,1 pointer to · · · cv2,n−1 pointer to cv2,n T
where each cvi,j is const, volatile, const volatile, or nothing. The
n-tuple of cv-qualifiers after the first in a pointer type, e.g.,
cv1,1, cv1,2, · · · , cv1,n in the pointer type T1, is called the
cv-qualification signature of the pointer type. An expression of type
T1 can be converted to type T2 if and only if the following conditions
are satisfied:
the pointer types are similar.
for every j > 0, if const is in cv1,j then const is in cv2,j , and similarly for volatile.
if the cv1,j and cv2,j are different, then const is in every cv2,k for 0 < k < j.

There are two rules here to note:
There are no implicit casts between T* and U* if T and U are different types.
You can cast T* to T const * implicitly. ("pointer to T" can be cast to "pointer to const T"). In C++ if T is also pointer then this rule can be applied to it as well (chaining).
So for example:
char** means: pointer to pointer to char.
And const char** means: pointer to pointer to const char.
Since pointer to char and pointer to const char are different types that don't differ only in const-ness, so the cast is not allowed. The correct type to cast to should be const pointer to char.
So to remain const correct, you must add the const keyword starting from the rightmost asterisk.
So char** can be cast to char * const * and can be cast to const char * const * too.
This chaining is C++ only. In C this chaining doesn't work, so in that language you cannot cast more than one levels of pointers const correctly.

Related

In the context of pointer to array conversions, can someone explain why those casts are legal or illegal?

Brushing up knowledge regarding (multidimensional) arrays / pointers conversions, the following two rules can explain some illegal conversions:
An T[M][N] decays to a T(*)[N], but not to a T** (as is explained in this SO entry)
There is not implicit conversion from T** to const T** (as is explained in the C++ faq)
So here is some test code written to try to cover different cases. The 3 cases we cannot explain are annotated with P1, P2 and P3.
int main() {
{
int arrayOfInt[3] = {0, 1, 2};
int * toPtr{nullptr};
int ** toPtrPtr{nullptr};
const int ** toPtrPtrConst{nullptr};
toPtr = arrayOfInt;
//toPtrPtr = &arrayOfInt; //KO, I assume because of 1.
//toPtrPtr = static_cast<int**>(&arrayOfInt); //KO, same as above
toPtrPtr = reinterpret_cast<int**>(&arrayOfInt);
toPtrPtr = (int**)&arrayOfInt;
//toPtrPtrConst = &arrayOfInt; //KO, still 1.
//toPtrPtrConst = static_cast<const int**>(&arrayOfInt); //KO, still 1.
toPtrPtrConst = reinterpret_cast<const int**>(&arrayOfInt); // (P1)
// it is supposed to be allowed to cast int** to const int* const*
// not const int**
// Why is it working without requiring a const_cast
// to cast away the const qualifier?
toPtrPtrConst = (const int**)&arrayOfInt;
//toPtrPtrConst = toPtrPtr; //KO, because of 2.
//toPtrPtrConst = reinterpret_cast<const int**>(toPtrPtr); //KO because of 2.
// so why is P1 allowed?
}
{
const int arrayOfConstInt[3] = {0, 1, 2};
const int * toPtrConst{nullptr};
const int ** toPtrPtrConst{nullptr};
int * const * toPtrConstPtr{nullptr};
toPtrConst = arrayOfConstInt;
//toPtrPtrConst = &arrayOfConstInt; //KO, I assume because of 1.
//toPtrPtrConst = static_cast<const int**>(&arrayOfConstInt); //KO, same as above
//toPtrPtrConst = reinterpret_cast<const int**>(&arrayOfConstInt); // (P2)
// Compiler error "casts away qualifiers",
// but which qualifier(s) would that cast away?
toPtrPtrConst = (const int**)&arrayOfConstInt;
//toPtrConstPtr = &arrayOfConstInt; //KO, I assume because of 1.
//toPtrConstPtr = static_cast<int * const *>(&arrayOfConstInt); //KO, same as above
toPtrConstPtr = reinterpret_cast<int * const *>(&arrayOfConstInt); // (P3)
// This one actually drops the const qualifier on the integer,
// but nevertheless it compiles
toPtrConstPtr = (int * const *)&arrayOfConstInt;
toPtrConstPtr = reinterpret_cast<int * const *>(&toPtrConst); // KO
// because it casts away const qualifier
// so why is P3 allowed?
}
}
Here it is in ideone: http://ideone.com/JzWmAJ
Why P1 allowed while it seems to violate 2.?
What are the qualifier(s) cast away by P2?
Why is P3 allowed when it actually casts away a const qualifier?
An explicit type conversion (a.k.a. "cast") doesn't violate a rule about implicit type conversions because rules about implicit type conversions are, to everyone's lack of surprise, not applicable to explicit type conversions.
const int (*)[3] is a type of a pointer-to-constant-something (where "something" is int[3]), whereas const int** is a type of a pointer-to-non-constant-something (where "something" is a pointer to a const int). The constant in pointer-to-constant-something gets stripped, which is not allowed.
No qualifier is stripped. toPtrConstPtr is a pointer-to-constant-something (where something is int *) and &arrayOfConstInt is a pointer-to-constant-something (where something is int[3]).
It should be noted that this is strictly language lawyer material. No normal programmer should allow any of these cast anywhere near their code.
All the casts in the code are bad, including all the ones that you didn't mark as bad.
If you do this:
cout << (uintptr_t)arrayOfInt << "\n";
cout << (uintptr_t)toPtrPtr << "\n";
you'll find that the output is identical. This is because &arrayOfInt has type int (*)[3]. When you dereference it with *&arrayOfInt, you get an array, which will decay back to a pointer of type int* with the same binary value as the pointer which you dereferenced. However, when you dereference an int**, you will load some bits from memory, and those bits will be the dereferenced pointer. These two dereferences are fundamentally incompatible. There is really no pretending that an int (*)[3] is the same as an int**.
T[N] are sometimes treated as a single type, as you use template-classes like std::array<T, N> or std::tuple<int, char, std::string>, and so it is treated like a single variable. Eg. jmp_buf of <csetjmp> is an array, but it is used like a normal variable. So while you are using fixed arrays in T[M][N], it is one linear block of N*M elements of type T as you write a member for each element in one struct, while pointer to pointer to T needs multiple memory-blocks, one memory-block for the pointer to the elements and the memory for each array pointed by the pointers. However arrays without fixed boundaries like T[] are treated like T*. When you use const T in an array, it works like a different type as T itsself, so you can c-cast an array of any type and if it is const or not, how you can cast char* to int*, c++ is strict and checks for const. The dereferenced arrayOfInt is an r-value, and reinterpret_cast sometimes has problems with r-values. Storing the dereferenced variable in a seperate variable and then reinterpret_cast it with the same type as a reference should work.

C/C++ convention for pointers and `const`

I have read this (Pasted below as well) on Wikipedia:
Following usual C convention for declarations, declaration follows use, and the * in a pointer is written on the pointer, indicating dereferencing. For example, in the declaration int *ptr, the dereferenced form *ptr is an int, while the reference form ptr is a pointer to an int. Thus const modifies the name to its right. The C++ convention is instead to associate the * with the type, as in int* ptr, and read the const as modifying the type to the left. int const * ptrToConst can thus be read as "*ptrToConst is a int const" (the value is constant), or "ptrToConst is a int const *" (the pointer is a pointer to a constant integer).
I really am not able to get a satisfying interpretation:
In which sense does it modify?
What is intended with name vs type (see the above link)?
And why should it be on the right side of const?
The const, volatile and restrict (C99 onwards) keywords are considered type qualifiers. They are an integral part of type signatures and describe additional semantics about a type.
If they appear at the topmost level of a declaration, they affect the declared identifier:
const int a = 5; // prevents modifications of "a"
int *const p = &x; // prevents modifications of "p", but not "*p"
int **const q = &y; // prevents modifications of "q", but not "*q" and "**q"
If they appear in pointer subtypes (before an asterisk), they affect the pointed-to value at the particular level of dereferencing:
const int *p = &x; // prevents modifications of "*p", but not "p"
const int **q = &y; // prevents modifications of "**q", but not "*q" and "q"
const int *const *r = &z; // prevents modifications of "**r" and "*r", but not "r"
const int *const *const s = &a; // prevents modifications of "**s", "*s" and "s"
The Wikipedia excerpt discusses two different conventions for declaring pointers:
int *p; // more common in C programming
int* p; // more common in C++ programming
I would say that the "true" convention is the first one because it works according to the syntax of the language (declarations mirror use). The asterisk in that declaration is in fact the same dereferencing operator that you would use on the pointer in normal expressions. Thus the int type is returned after applying * (indirection) on p (the pointer itself).
Also note that the ordering of type qualifiers with respect to type specifiers and other type qualifiers does not matter, so these declarations are equivalent:
const int a; // preferred
int const a; // same, not preferred
const volatile int b; // preferred
volatile const int b; // same, not preferred
volatile int const b; // same, not preferred
const int *p; // preferred
int const *p; // same, not preferred
In which sense does it modify?
Modifies in the sense that it makes it constant, meaning it can't be modifed (assigned to, or passed to a function which might modify it).
What is intended with name vs type (see the above link)?
Name means the word written in the source code here, I think.
And why should it be on the right side of const?
"Modifies name to its right" means, by example:
const char * str, here const modifies char, in other words the characters are constant, you can't modify them. You can make str point to a new char, but you still can't modify it either (at least not through str). *str = 'a'; is compiler error, str = "foo"; is ok.
char * const str, here const modifies str, in other words the value of str can't be modified. It points to some char, and you can modify that char through str, but you can't make str to point to another char. *str = 'a'; is now ok, str = "foo"; is error.

error: invalid conversion from ‘char**’ to ‘const char**’ [-fpermissive] [duplicate]

It is legal to convert a pointer-to-non-const to a pointer-to-const.
Then why isn't it legal to convert a pointer to pointer to non-const to a pointer to pointer to const?
E.g., why is the following code illegal:
char *s1 = 0;
const char *s2 = s1; // OK...
char *a[MAX]; // aka char **
const char **ps = a; // error!
From the standard:
const char c = 'c';
char* pc;
const char** pcc = &pc; // not allowed
*pcc = &c;
*pc = 'C'; // would allow to modify a const object
Ignoring your code and answering the principle of your question, see this entry from the comp.lang.c FAQ:
Why can't I pass a char ** to a function which expects a const char **?
The reason that you cannot assign a char ** value to a const char ** pointer is somewhat obscure. Given that the const qualifier exists at all, the compiler would like to help you keep your promises not to modify const values. That's why you can assign a char * to a const char *, but not the other way around: it's clearly safe to "add" const-ness to a simple pointer, but it would be dangerous to take it away. However, suppose you performed the following more complicated series of assignments:
const char c = 'x'; /* 1 */
char *p1; /* 2 */
const char **p2 = &p1; /* 3 */
*p2 = &c; /* 4 */
*p1 = 'X'; /* 5 */
In line 3, we assign a char ** to a const char **. (The compiler should complain.) In line 4, we assign a const char * to a const char *; this is clearly legal. In line 5, we modify what a char * points to--this is supposed to be legal. However, p1 ends up pointing to c, which is const. This came about in line 4, because *p2 was really p1. This was set up in line 3, which is an assignment of a form that is disallowed, and this is exactly why line 3 is disallowed.
And as your question is tagged C++ and not C, it even explains what const qualifiers to use instead:
(C++ has more complicated rules for assigning const-qualified pointers which let you make more kinds of assignments without incurring warnings, but still protect against inadvertent attempts to modify const values. C++ would still not allow assigning a char ** to a const char **, but it would let you get away with assigning a char ** to a const char * const *.)
Just since nobody has posted the solution, here:
char *s1 = 0;
const char *s2 = s1; // OK...
char *a[MAX]; // aka char **
const char * const*ps = a; // no error!
(http://www.parashift.com/c++-faq-lite/const-correctness.html#faq-18.17 for why)
The C++11 draft standard explains this in a note in section 4.4 which says:
[ Note: if a program could assign a pointer of type T** to a pointer
of type const T** (that is, if line #1 below were allowed), a program
could inadvertently modify a const object (as it is done on line #2).
For example,
int main() {
const char c = 'c';
char* pc;
const char** pcc = &pc; // #1: not allowed
*pcc = &c;
*pc = 'C'; // #2: modifies a const object
}
—end note ]
An interesting related question is Given int **p1 and const int **p2 is p1 == p2 well formed?.
Note the C++ FAQ also has an explanation for this but I like the explanation from the standard better.
The conforming text that goes with the note is as follows:
A conversion can add cv-qualifiers at levels other than the first in
multi-level pointers, subject to the following rules:56
Two pointer types T1 and T2 are similar if there exists a type T and
integer n > 0 such that:
T1 is cv1,0 pointer to cv1,1 pointer to · · · cv1,n−1 pointer to cv1,n
T
and
T2 is cv2,0 pointer to cv2,1 pointer to · · · cv2,n−1 pointer to cv2,n T
where each cvi,j is const, volatile, const volatile, or nothing. The
n-tuple of cv-qualifiers after the first in a pointer type, e.g.,
cv1,1, cv1,2, · · · , cv1,n in the pointer type T1, is called the
cv-qualification signature of the pointer type. An expression of type
T1 can be converted to type T2 if and only if the following conditions
are satisfied:
the pointer types are similar.
for every j > 0, if const is in cv1,j then const is in cv2,j , and similarly for volatile.
if the cv1,j and cv2,j are different, then const is in every cv2,k for 0 < k < j.
There are two rules here to note:
There are no implicit casts between T* and U* if T and U are different types.
You can cast T* to T const * implicitly. ("pointer to T" can be cast to "pointer to const T"). In C++ if T is also pointer then this rule can be applied to it as well (chaining).
So for example:
char** means: pointer to pointer to char.
And const char** means: pointer to pointer to const char.
Since pointer to char and pointer to const char are different types that don't differ only in const-ness, so the cast is not allowed. The correct type to cast to should be const pointer to char.
So to remain const correct, you must add the const keyword starting from the rightmost asterisk.
So char** can be cast to char * const * and can be cast to const char * const * too.
This chaining is C++ only. In C this chaining doesn't work, so in that language you cannot cast more than one levels of pointers const correctly.

Assigning pointers to pointers with or without qualifiers [duplicate]

This question already has answers here:
constness and pointers to pointers
(4 answers)
Closed 9 years ago.
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?
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
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;

Explain: Converting 'char **' to 'const char **', Conversion loses qualifiers [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Implicit cast from char** to const char**
Given the following code:
void foo( const char ** buffer );
void bar()
{
char * buffer;
foo( &buffer );
}
Why is it that if the foo() function has a const char * parameter the compiler doesn't complain when passing in a char * variable into it? But when using char **, it cannot convert it to const char **? Does the compiler add any const qualifiers in the former case?
I've read section 4.4 of the C++ standard and it just confused me further.
Yes, you cannot implicitly convert from a T ** to a const T **, because the compiler can no longer guarantee that the const-ness won't be violated.
Consider the following code (borrowed from the C FAQ question on exactly this topic: Why can't I pass a char ** to a function which expects a const char **?):
const char c = 'x';
char *p1;
const char **p2 = &p1; // 3
*p2 = &c;
*p1 = 'X'; // 5
If the compiler allowed line 3, then line 5 would end up writing to a const object.
Consider:
char const someText[] = "abcd";
void
foo( char const** buffer )
{
*buffer = someText;
}
void
bar()
{
char* buffer;
foo( &buffer );
*buffer = 'x';
}
If this were legal, it would be possible to modify a const object
without an intervening const_cast. The conversion is forbidden
because it violates const-ness.
You are probably confusing the level of indirection the const applies to.
A char** can be described as pointer to a pointer to a character whereas a const char** can be described as pointer to a pointer to a constant character.
So when we write this differently, we have pointer to A (where A = pointer to character) and we have pointer to B (where B = pointer to a constant character).
Clearly now (I hope) A and B are distinct types, as such a pointer to A can not be assigned toa a pointer to B (and vice versa).