Подтвердить что ты не робот

Поскольку строковый литерал считается lvalue, почему ссылка привязки lvalue будет const?

Я знаю, что есть темы, похожие на это уже (например, this).

Пример, приведенный в этом разделе, был следующим:

std::string & rs1 = std::string();

Ясно, что std::string() является значением r. Однако, мой вопрос в том, почему s1 является законным, а s2 не является?

const std::string& s1 = "String literal";
std::string& s2 = "String literal";

В стандарте четко указано, что строковые литералы являются lvalues ​​(что понятно, поскольку они являются технически const char * за кулисами). Когда я скомпилирую s2, я получаю следующее:

prog.cpp:4:19: error: invalid initialization of non-const reference of type
'std::string& {aka std::basic_string<char>&}' from an rvalue of type
'const char*' std::string& s2 = "String literal";

Я понимаю, что стандартное определение lvalues ​​и rvalues ​​является взаимоисключающим, так это потенциально ошибка с компилятором? Я использую gcc 4.9.2 в этом примере. Будет ли это также одним из случаев, когда литерал действительно является значением xvalue?

4b9b3361

Ответ 1

Проблема заключается в том, что строковый литерал не имеет тип std::string или его подкласс - он имеет тип char const[N]. Таким образом, тип инициализатора не является ссылочным, совместимым с целевым типом ссылки, а необходимо создать временное и привязать к ссылке.

Тем не менее, временные ссылки не могут быть привязаны к не константным ссылкам. То есть ваш случай эквивалентен

std::string& s = std::string("Abcdefg");

который, по вашему мнению, явно плохо сформирован.


Собственно, точная причина, по которой он не работает, заключается не в том, что временные лица не могут быть привязаны к неконстантным ссылкам lvalue, а скорее к тому, что инициализатор ссылки не const const подчинен определенным требованиям, которые char const[N] не может встречаться в этот случай, [dcl.init.ref]/5:

Ссылка на тип "cv1 T1" инициализируется выражением введите "cv2 T2" следующим образом:

  • Если ссылка является ссылкой lvalue и выражением инициализатора

    • является lvalue (но не является битовым полем), а "cv1 T1" ссылается на "cv2 T2" или
    • имеет тип класса (т.е. T2 - тип класса), где T1 не ссылается на T2 и может быть неявно преобразован в lvalue типа "cv3 T3", где "cv1 T1" является ссылочной совместимостью с "cv3 T3" 106 (это преобразование выбирается путем перечисления применимого преобразования (13.3.1.6) и выбора наилучшего разрешение перегрузки (13.3)),

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

  • В противном случае ссылка должна быть ссылкой lvalue на нелетучий const-тип (т.е. cv1 должен быть const) или ссылкой должна быть ссылкой rvalue.

    • [..]

106) Для этого требуется функция преобразования (12.3.2), возвращающая ссылочный тип.

Ответ 2

Строковый литерал может быть lvalue, но это не объект string. Там создается временная string, которая является rvalue.

Ответ 3

Сначала строка представляет собой const char * или const char [N] не a std::string. Таким образом, вы не напрямую назначаете эти строки char *. std::string имеет конструктор, который принимает const char [N], и компилятор автоматически использует его для создания нового экземпляра.

Но при использовании const для s2 вы просто не смогли компилятору назначить новый экземпляр std::string для s2.

Итак, в конце концов, я думаю, вы неправильно поняли разницу между "строкой" типа const char [N] и "строкой" типа std::string. Стандарт ссылается на const char [N], когда речь идет о том, что строки являются lvalue, но вы пытаетесь применить это к std::string, к которому стандартное правило не применяется.