Почему const char * const & = "hello" компилируется?
Я читаю фрагмент кода из книги и нахожу это:
const char* const & a = "hello"; //can compile
const char*& a = "hello"; //cannot
Все, что я знаю, это то, что при инициализации ссылки преобразование с указателем на указатель не произойдет.
const char* const &
, ссылка на const pointer
, указатель указывает на const char
.
const char*&
, ссылка на pointer
, указатель указывает на const char
.
Итак, почему добавление дополнительной const
, указывающее, что указатель является const
, позволяет компилировать?
Ответы
Ответ 1
Он по существу придерживается этой формулы
T const & a = something_convertible_to_T;
Где T - const char*
. В первом случае временный указатель может быть материализован, назначен адрес литерала, а затем привязан к ссылке. Во втором случае, поскольку ссылка lvalue не const, это не может произойти. Другой пример более того же
const char* && a = "hello"; // rvalue ref makes a no into a yes.
Теперь временный указатель привязан к ссылке rvalue.
Ответ 2
Некоторая дополнительная фраза для скуки, после прочтения превосходного ответа от @StoryTeller, поскольку мне пришлось пройти через другой процесс размышлений об этом.
Синтаксически синтаксически. В обеих строках мы определяем ссылку a
, и в обоих мы будем иметь материализацию временного указателя, который берет адрес строкового литерала. Единственное отличие между ними состоит в том, что второй const
появляется только здесь:
const char* const & a = "hello";
и не здесь:
const char*& a = "hello";
Этот 2-й const
означает, что объект, на который делается ссылка здесь, указатель в этом случае сам по себе является const, так как в нем его нельзя изменить с использованием этой ссылки.
Следовательно, поскольку тип этого строкового литерала является const char[6]
(а не const char *
например), наша ссылка lvalue
на тип const char*
во второй строке не может привязываться к нему, но ссылка в первой строке, являясь ссылкой на тип const char* const
. Зачем? Из-за правил ссылочной инициализации:
(5) Ссылка на тип "cv1 T1" инициализируется выражением типа "cv2 T2" следующим образом:
Оба выражения являются lvalues, но наш "cv1 T1" не является ссылочным-совместимым с нашими "cv2 T2" и "T2" не является типом класса.
- (5.2) В противном случае, если ссылка является ссылкой lvalue на тип, который не является const-квалифицированным или является нестабильным, программа плохо сформирована
Ссылка действительно не является const-свойством: наш "T1" представляет собой const char*
, который является указателем на const, в отличие от указателя const. Фактический тип здесь - тип указателя, так что это важно.
Ошибка Clang для второй строки, прочитанная с учетом этого, говорит нам именно об этом:
error: non-const lvalue reference to type 'const char *' cannot bind to a value of unrelated type 'const char [6]'
const char*& a = "hello";
^ ~~~~~~~
Часть о том, что ссылка не const const равна точно ¶5.2 - lvalue в нашем случае является указателем на const
, но сама по себе не const! В отличие от первой строки. Часть о привязке к несвязанному типу точно равна ¶5.1 - наш const char*
несовместим с RHS, являющимся const char[6]
, или const char* const
после преобразования массива в указатель.
По этой точной причине или ее отсутствию это может быть без ошибок:
char* const & a = "hello";
ISO С++ 11 в стороне, компилятор позволяет этому пройти (не то, что он должен, поскольку строковый литерал является "const char 6 ", и мы не должны отбрасывать этот первый const
), поскольку ссылка теперь const
с относится к его объекту, указателю.
Еще одна интересная вещь: ссылка rvalue
const char* && a
(no "2nd const
") может привязываться к временному указателю, который был реализован из строкового литерала, как предоставил @StoryTeller. Это почему? Из-за правил преобразования массива в указатель:
Lvalue или rvalue типа "массив NT" или "массив неизвестной границы T" можно преобразовать в prvalue типа "указатель на T".
Здесь нет упоминания о const
или другой cv-qualification, но это стоит только до тех пор, пока мы инициализируем rvalue ref.