Поскольку строковый литерал считается 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?
Ответы
Ответ 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
, к которому стандартное правило не применяется.