vpozdyayev
vpozdyayev

Reputation: 1044

Const vs. array-to-pointer conversions

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

Answers (4)

Aaron McDaid
Aaron McDaid

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

Vlad from Moscow
Vlad from Moscow

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

Mike Seymour
Mike Seymour

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

vsoftco
vsoftco

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

Related Questions