Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't a const T*& bind to a T*?

Tags:

c++

VS2010 shows error C2440: 'return' : cannot convert from 'char *' to 'const char *& in f3 below. Why is it not possible to return a char* when the return type is const char*&?

const char* const& f1()
{
    static char* x = new char[5]();
    return x;
}

char* const& f2()
{
    static char* x = new char[5]();
    return x;
}

const char* & f3()
{
    static char* x = new char[5]();
    return x;    // error C2440
}

char* & f4()
{
    static char* x = new char[5]();
    return x;
}
like image 374
Belloc Avatar asked Mar 27 '13 21:03

Belloc


2 Answers

From Paragraph 8.5.3/2 of the C++11 Standard:

A reference cannot be changed to refer to another object after initialization. Note that initialization of a reference is treated very differently from assignment to it. Argument passing (5.2.2) and function value return (6.6.3) are initializations.

This basically tells you that returning a value from a function is equivalent to performing an initialization. Therefore, function f3() doesn't compile for the same reason the last initialization in the code snippet below doesn't compile:

char c = 'a';
char* x = &c;
const char*& y = x; // ERROR!

The type of the object referenced by y is const char*, while the type of the expression we are trying to initialize it with (i.e. the type of x) is char*. Those are different types, and when binding a reference the types of the initializer expression and of the object referenced by the initialized reference must be identical (with an exception for base and derived classes, which are not involved here), apart from top-level cv qualifications.

Here, the const qualification in const char* is not a top-level qualification, because it applies to the pointed object, and not to the pointer itself: while the pointed char value cannot be modified through a const char* pointer, the pointer itself can be reassigned.

In Standard terms, this means that the types const char* and char* are not reference-related:

Given types “cv1 T1” and “cv2 T2,” “cv1 T1” is reference-related to “cv2 T2” if T1 is the same type as T2, or T1 is a base class of T2. “cv1 T1” is reference-compatible with “cv2 T2” if T1 is reference-related to T2 and cv1 is the same cv-qualification as, or greater cv-qualification than, cv2. [...]

On the other hand, function f2() compiles, consistently with the fact that the initialization in the last line below is legal:

char c = 'a';
char* x = &c;
char* const& y = x; // OK

Here, the type of the referenced object is char* const, which (unlike const char*) is reference-compatible with char* in the sense defined by the Paragraph quoted above *(the const qualification is a top-level qualification in this case).

In Particular, per 8.5.3/5:

A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows:

— If the reference is an lvalue reference and the initializer expression

  • is an lvalue (but is not a bit-field), and “cv1 T1” is reference-compatible with “cv2 T2,” or

[...]

The omitted part is not relevant to this case. Here, char* const and char* are reference-compatible, and the initialization is legal (and f2() compiles). On the other hand, const char* and char* are not reference-compatible, and the initialization is illegal (and f3() does not compile).

like image 155
Andy Prowl Avatar answered Oct 26 '22 08:10

Andy Prowl


For the same reason you can't cast from char ** to const char **. Otherwise you could write

f3() = "hello";

and a subsequent call to f3 would be able to write to the memory of the string literal, aliased to the static local x.

like image 25
ecatmur Avatar answered Oct 26 '22 07:10

ecatmur