Reputation: 1044
Is the handling of constness in fa
being turned inside out intentional, or does it look like a bug (compiler or the standard)? I can see how this can happen, but it still feels pretty weird (at the very least I would expect a = "qwe"
to be treated as an error, too---like in fs
).
typedef char A[8];
typedef char *P;
typedef std::string S;
void fa( const A a ) {
a = "qwe"; // ok
// *a = 'Q'; // error
}
void fp( const P p ) {
// p = "qwe"; // error
*p = 'Q'; // ok
}
void fs( const S s ) {
// s = "qwe"; // error
// *s = 'Q'; // error
}
[MinGW 4.9.1]
CLARIFICATION: I understand the general way this conversion works, hence the "I can see how this can happen" part. What this question is about, is (a) std references (thanks to those who provided them), and (b) if there is an actual rationale for this quirk (which is pretty counter-intuitive).
Upvotes: 0
Views: 88
Reputation: 27133
Arrays inside parameter lists are weird.
void foo(char a[10], char b) {
char c[10];
char d;
}
As you might expect, sizeof(d)
and sizeof(b)
are both 1
. And sizeof(c)==10
, as expected.
But sizeof(a)
will be either or 4 or 8, depending on the number of bits of your platform. It will be the size of a pointer.
So basically, an array inside a parameter list is different from an array anywhere else.
So c
really is an array, and you cannot do c = "hi";
.
But the compiler will silently replace arrays in parameters with a pointer. So void foo(char a[10], char b)
is rewritten as void foo(char *a, char b)
.
And the const
is applied to the array before it is rewritten as a pointer - so you get a non-const pointer to const characters.
The lesson here is that arrays in parameters are basically useless. All size information is lost. The compiler won't stop you passing an array of large size to an array parameter of smaller size.
The only useful way to have an array in a parameter list is as a reference.
void foo(char (&a)[10], b) {
}
In this case, you will get an error message if you pass an array that doesn't have exactly 10 elements. And you will get the behaviour you expect if you assign to the array or to an element of the array.
And you can use templates to 'remember' the size of the array if you need to know.
template<size_t N>
void foo(char (&a)[N]) {
....
}
Upvotes: 0
Reputation: 310970
This function declaration
void fa( const A a );
is equivalent to
void fa( const char a[8] );
that in turn is equivalent to
void fa( const char *a );
and all of them declare the same one function Within the function the parameter has type const char *
as in the last function declaration.
On the other hand string literals have types of constant character arrays that in expressions are also implicitly converted to pointers to their first elements.
Thus in thsi assignment
a = "qwe"; // ok
string literal "qwe" that has type const char[4]
is implicitly converted to type const char *
.
So the left side of the assignment has type const char *
and the right side of the assignment has the same type const char *
.
So there is no problem.
Take into account that in this declaration
const A a
typedef name is substituted for the original type and you get
const char[8]
Take into account that according to the C++ Standard (3.9.3 CV-qualifiers)
2 ... Any cv-qualifiers applied to an array type affect the array element type, not the array type (8.3.4).
On the other hand in this declaration
const P p
P is substituted to char *
and you get char * const
that is you get constant pointer that may not change its value.
Upvotes: 1
Reputation: 254461
This is correct: the parameter type of fa
is const char *
, not char * const
. This a subtlety of the meaning of an array declaration, and the adjustment of an array type to a pointer type as a function argument.
First the array type itself is adjusted:
C++11 8.3.4/1: Any type of the form “cv-qualifier-seq array of N T” is adjusted to “array of N cv-qualifier-seq T”
so initially, the type becomes "array of const char
". Then, since it's a function parameter, the type is further adjusted:
C++11 8.3.5/5: any parameter of type “array of T” [...] is adjusted to be “pointer to T”
So the final type is "pointer to const char
", or const char *
. Hence, as your example shows, you can modify the pointer, but not the data it point to.
For the other examples, the parameter types are themselves const
: for fp
it's char * const
, allowing modification of the data but not the pointer; and for fp
it's const string
, allowing no modification at all.
Upvotes: 2
Reputation: 56547
A
has type char[8]
. const A
is const char[8]
. The parameter of fa
is therefore const char[8]
, which decays to a pointer const char*
, so everything is fine in fa
, as you are assigning a string literal (of type const char[SIZE]
) to the pointer a
).
P
has type char*
. const P
has type char* const
(not const char*
), as the const is applied to the pointer. So you cannot assign p
to some other pointer. *p = 'Q'
is OK since you are assigning to the location the pointer points to. Hence the second function fb
also behaves as expected.
Upvotes: 3