У меня возникли некоторые трудности с интерпретацией пункта 5.2.1.1 в параграфе §8.5.3/5 N4140.
Фрагмент ниже компилирует
#include <iostream>
int& f() { static int i = 100; std::cout << i << 'n'; return i; }
int main()
{
int& r = f();
r = 101;
f();
}
И вывести значения (живой пример)
100
101
Теперь, читая §8.5.3/5 в N4140, я вижу, что он компилируется из-за точки маркера (5.1.1), то есть ссылка является ссылкой lvalue, выражение инициализатора является lvalue и int
совместимо с int
(или с int&
- я не знаю точно, какой из них я должен использовать здесь).
Пуля очков (5.1) и (5.1.1):
Теперь предположим, что я изменяю левую ссылку на значение в объявлении- Если ссылка - это ссылка lvalue и выражение инициализатора
— is an lvalue (but is not a bit-field), and “cv1 T1” is reference-compatible with “cv2 T2,” or ...
int& r = f();
на правую ссылку на значение, т. е. int&& r = f();
. Я знаю, что код не будет компилироваться, так как ссылка rvalue не привязывается к lvalue. Но мне любопытно, как прийти к такому выводу, используя стандарт?
Я объясню, в чем мои трудности:
- очевидно, что
int&& r = f();
покрыто точкой маркера (5.2), потому что ссылка - это ссылка rvalue.
Пункт маркера (5.2):
- В противном случае ссылка должна быть ссылкой lvalue на a энергонезависимый тип const (то есть cv1 должен быть const), или ссылка будет ссылкой rvalue.
- в принципе, я бы сказал, что (5.2.1.1) поддерживает эту инициализацию, поскольку инициализатор является функцией lvalue и
int
является ссылочной совместимой сint
(или сint&
).
Пункты маркера (5.2.1) и (5.2.1.1):
- Если выражение инициализатора
— is an xvalue (but not a bit-field), class prvalue, array prvalue or function lvalue and “cv1 T1” is reference-compatible with “cv2 T2”, or ...
Edit
Я включил дословно маркированные точки из N4140 (C++14), которые эквивалентны аналогичным маркированным точкам в N3337 (C++11).
1 ответ:
Выражение инициализатора является lvalue и
int
является ссылочно-совместимым сint
(или сint&
- я не знаю точно, какой из них я должен использовать здесь).Ссылочная совместимость-это отношение, применяемое к указанному типу, а не к ссылочному типу. Например, [dcl.в этом.ref] / 5 говорит об интиализации " ссылки на тип cv1
T1
выражением типа cv2T2
", и позже сравнивает, например, " гдеT1
не имеет отношения к ссылкеT2
".Тип выражения
f()
является простоint
, несмотря на то, что возвращаемый типf
являетсяint&
. Выражения просто не имеют ссылочного типа, когда мы их наблюдаем(*); ссылка отсекается и используется для определения категории ценности (см. [expr]/5). Дляint& f()
выражениеf()
является значением lvalue; дляint g()
выражениеg()
является значением rvalue.(*)чтобы быть совершенно точным, выражения могут иметь ссылочный тип в Стандартный , но только в качестве" начального " результирующего типа. Ссылка отбрасывается "до любого дальнейшего анализа", что подразумевает, что эта ссылочность просто не наблюдаема через тип.
Теперь предположим, что я изменяю левую ссылку на значение в объявленииПутаница, как видно из обсуждения в комментариях, по-видимому, заключается в том, чтоint& r = f();
на правую ссылку на значение, т. е.int&& r = f();
. Я знаю, что код не будет компилироваться, так как ссылка rvalue не привязывается к lvalue. Но мне любопытно, как прийти к этому выводу, используя Стандарт?f()
не является функцией lvalue. Категории ценностей, как "значение" и "значение rvalue" свойства выражений. Поэтому термин " функция lvalue "должен относиться к выражению, а именно к выражению типа функции с категорией значений"lvalue" .Но выражение
f()
является выражение вызова функции. Грамматически, это postfix-выражение , постфиксом которого является список аргументов функции. Согласно [expr.вызов] / 10:Вызов функции является значением lvalue, если тип результата является ссылочным типом lvalue или ссылкой rvalue на тип функции, xvalue, если тип результата является ссылкой rvalue на тип объекта, и prvalue в противном случае.
И [expr.вызов]/3
Если постфиксное выражение обозначает деструктор [...]; в противном случае тип функции выражение вызова-это возвращаемый тип статически выбранной функции [...]
То есть (наблюдаемый см. выше) тип выражения
f()
являетсяint
, а категория значения - "lvalue". Обратите внимание, что (наблюдаемый) тип неint&
.Значение функции lvalue - это, например, id-выражение , подобное
f
, результат косвенного указания указателя функции или выражение, дающее любой вид ссылки на функция:using ft = void(); void f(); ft& l(); ft&& r(); ft* p(); // function lvalue expressions: f l() r() *p()
[expr.подтянутый.general] / 8 указывает, что такие идентификаторы, как
f
, являются, как id-выражениями, lvalues:Идентификаторявляется id-выражениемпри условии, что оно было соответствующим образом объявлено. [...] Тип выражения - это тип идентификатора . Результатом является сущность, обозначаемая идентификатором. Результатом является значение lvalue, если сущность является функцией, переменной или членом данных и prvalue иначе.
Вернемся к примеру
int&& r = f();
. Используя какой-то проект после N4296.[dcl.в этом.ref]
5 ссылка на тип " cv1
T1
" инициализируется выражением типа "cv2T2
" следующим образом:
- (5.1) если ссылка является ссылкой lvalue и выражением инициализатора
Ссылка является ссылкой rvalue. 5.1 не применяется.
- (5.2) в противном случае ссылка является ссылкой lvalue на a энергонезависимый тип const (например, cv1 должен быть
const
), или ссылка будет ссылкой rvalue. [пример опущен]Это применимо, ссылка является rvalue-ссылкой.
- (5.2.1) если выражение инициализатора
- (5.2.1.1)-это xvalue (но не битовое поле), класс prvalue, массив prvalue или функция lvalue и [...], или
- (5.2.1.2) имеет тип класса (т. е.
T2
является типом класса) [...]Инициализатор-это lvalue типа
int
. 5.2.1 не применяется.
- (5.2.2) в противном случае:
- (5.2.2.1) если
T1
илиT2
является типом класса [...]- (5.2.2.2) в противном случае-временный тип " cv1
T1
" создается и инициализируется копированием (dcl.init) из выражения инициализатора. Ссылка затем привязывается к временному.Наконец, применяется пункт 5.2.2.2. Однако:
Если
T1
относится кT2
:
- (5.2.2.3) почтовый индекс CV1 должен быть таким же резюме-квалификация, или больше резюме квалификация, чем, cv2; и
- (5.2.2.4) если ссылка является ссылкой rvalue, выражение инициализатора не должно быть значением lvalue.
T1
иT2
являютсяint
(the ссылка возвращаемого типаf()
удаляется и используется только для определения категории значений), поэтому они связаны с ссылками. cv1 и cv2 оба пусты. ссылка является ссылкой rvalue, аf()
- lvalue, следовательно, 5.2.2.4 делает программу плохо сформированной.
Причина, по которой термин "значение функции lvalue" появляется в 5.2.1.1, может быть связана с проблемой "значений функции rvalue" (см., например, N3010 - ссылки на Rvalue как " забавные" Lvalues ). В C++03 не было значений функций rvalues, и, похоже, комитет не хотел вводить их в C++11. Без ссылок на rvalue, я думаю, что невозможно получить функцию rvalue. Например, вы не можете привести к типу функции и не можете возвращать типы функций из функции.
Вероятно, для согласованности значения функций lvalue могут быть привязаны к ссылкам rvalue на типы функций через приведение:
template<typename T> void move_and_do(T& t) { T&& r = static_cast<T&&>(t); // as if moved } int i = 42; move_and_do(i); move_and_do(f);
Но для
T
, являющегося функцией типаvoid()
, категория значенияstatic_cast<T&&>(t)
является lvalue (отсутствуют значения типа функции). Следовательно, ссылки rvalue на типы функций могут быть привязаны к значениям функций lvalue.