2013-03-27 3 views
5

VS2010 показывает ошибку C2440: 'return' : cannot convert from 'char *' to 'const char *& в f3 ниже. Почему невозможно вернуть char*, когда тип возврата const char*&?Почему const T * не может связываться с T *?

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; 
} 

ответ

5

из пункта 8.5.3/2 C++ 11 стандарта на:

Ссылка не может быть изменена, чтобы ссылаться на другой объект после инициализации. Обратите внимание, что инициализация ссылки трактуется совсем иначе, чем присвоение ей. Прохождение аргумента (5.2.2) и Значение функции return (6.6.3) - это инициализации.

В основном это говорит о том, что возврат значения из функции эквивалентно выполнению инициализации. Таким образом, функция f3() не компилируется по той же причине последней инициализации в фрагменте кода ниже не компилировать:

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

Тип объекта, на который ссылается y является const char*, в то время как тип выражения мы пытаются инициализировать его (т. е. тип x) - char*. Это различных типов, и при привязке ссылки типы выражения инициализатора и объекта, на который ссылается инициализированная ссылка , должны быть идентичными (за исключением базовых и производных классов, которые здесь не задействованы), кроме верхний уровень cv квалификация.

Здесь const квалификации в const char* является не квалификацией верхнего уровня, так как он относится к остроконечному предмету, а не к самому указателю: в то время как остроконечные char значение не может быть изменено через const char* указатель, то сам указатель может быть переназначен.

В стандартных условиях, это означает, что типы const char* и char* не являются ссылки, связанные с:

Указанные типы «cv1 T1» и «cv2 T2», «cv1 T1» является ссылки, связанные с к «cv2 T2», если T1 является того же типа, что иT2, или T1 является базовым классом T2.«CV1 Т1» является справочника-совместимым с «cv2 T2», если T1 является ссылка связанных сT2 и cv1 одно и то же резюме-квалификация, как, или больше резюме, чем-квалификация, cv2. [...]

С другой стороны, функция f2() компилирует, последовательно с тем, что инициализация в последней строке ниже законна:

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

Здесь, тип ссылочного объекта составляет char* const, который (в отличие от const char*) является ссылочным-совместимым с char* в смысле, определяемом приведенным выше абзацем * (квалификация const - квалификация в этом случае).

В частности, за 8.5.3/5:

Ссылка на тип «CV1 T1» инициализируется выражением типа «CV2 Т2» следующим образом:

- Если ссылки является ссылка Левой и выражение инициализатора

  • является именующей (но не битовым поле), и «CV1 Т1» является ссылкой совместит с «CV2 Т2,» или

[...]

Пропущенная часть не имеет отношения к этому делу. Здесь char* const и char* являются эталонной совместимостью, а инициализация является законной (и f2() компиляции). С другой стороны, const char* и char*: неСсылка, и инициализация незаконна (и f3() не компилируется).

+0

Что такое квалификация высшего уровня? Как вы охарактеризовали термин «верхний уровень» в этом контексте? – Belloc

+0

@ user1042389: Я попробую пример: в 'int const' и' int * const', 'const' - это верхний уровень; в 'int const *' (который является таким же, как 'const int *'), 'const' не является верхним уровнем, потому что он применяется к указанному« int », а не к самому указателю. В общем, на C++ вы можете создавать сложные типы с помощью таких конструкций, как «указатель на», «ссылка на», «массив» и т. Д. Когда вы создаете тип таким образом (например, «ссылка на указатель на указатель« char »)) ваш тип имеет иерархическую структуру (цепочка, в нашем случае). Квалификатор верхнего уровня - это квалификатор, который применяется к верхнему узлу этой структуры –

2

По той же причине вы не можете вылитой из char ** в const char **. В противном случае вы могли бы написать

f3() = "hello"; 

и последующий вызов f3 будет иметь возможность записывать в память строкового литерала, псевдоним статического локального x.

+0

Хм, и почему f3() = "привет"; компилировать? f3() является const или? Он считается как в объявлении i в const int & i =b;? – qPCR4vir

+0

'f3() =" hello "' компилируется, потому что ваш конкретный компилятор позволяет вам делать что-то немое по причинам, связанным с наследием. Многие API-интерфейсы C имели интерфейсы 'char * ', которые они обещали бы не писать. Таким образом, позволяя струнным литералам превращаться в 'char *' беззвучно, C API все еще работает. – Yakk

2
#include <iostream> 
void problem(const char*& output, const char* input) 
{ 
    output = input; // legal, const char*& = const char* 
} 

int main() { 
    const char buff[] = "hello"; 
    char* out = nullptr; 
    problem(const_cast<const char*&>(out), &buff[0]); 
    out[0] = 'H'; // I just changed a const array's first character 
    std::cout << &buff[0] << "\n"; 
} 

Отпечаток "Hello", что является доказательством того, что я просто занимался неопределенным поведением. Для этого мне пришлось использовать const_cast. Если вы можете инициализировать const char*& с помощью char*, мне не нужно было бы делать const_cast для вызова неопределенного поведения.

«Но, можно сказать,« это не соответствует точному случаю, который опубликовал ОП! »

Это правда.

// the "legal" way to do what the OP wants to do, but it leads to undefined 
// behavior: 
const char*& problem2(char*& c) { return const_cast<const char*&>(c); } 
void problem3(const char*& left, const char* right) { left = right; } 

#include <iostream> 
int main() { 
    char* c = nullptr; 
    char const* buff = "hello undefined behavior"; 
    problem3(problem2(c), buff); 
    c[0] = 'H'; 
    std::cout << buff << "\n"; 
} 

также печатает "Hello undefined behavior".

«Но», можно сказать, «вы превратили char*& в const char*&, а не char* в const char*&.

Очень хорошо:

void problem3(const char*& left, const char* right) { left = right; } 
const char*& problem4(char x = 0) { 
    static char* bob = nullptr; 
    if (bob && x) 
    bob[0] = x; 
    return const_cast<const char*&>(bob); 
} 
#include <iostream> 
int main() { 
    char const* buff = "hello problem"; 
    problem3(problem4(), buff); 
    problem4('H'); 
    std::cout << buff << "\n"; 
} 

Опять же, что компрометирующие капитал H (ну, на самом деле, неопределенное поведение, которое обычно является столицей H).

Смежные вопросы