Изучая определение категорий значений, я попробовал предложенный здесь фрагмент: https://stackoverflow.com/a/16638081/21691539
template <typename T>
struct value_category {
// Or can be an integral or enum value
static constexpr auto value = "prvalue";
};
template <typename T>
struct value_category<T&> {
static constexpr auto value = "lvalue";
};
template <typename T>
struct value_category<T&&> {
static constexpr auto value = "xvalue";
};
// Double parens for ensuring we inspect an expression,
// not an entity
#define VALUE_CATEGORY(expr) value_category<decltype((expr))>::value
Но тестируя его, я не понимаю его вывода в ситуации, подразумевающей ссылки. Вот мой «тестовый стенд»:
#include <iostream>
struct S {
int val;
int& rval = val;
};
int f();
int& rf();
// VALUE_CATEGORY as in previous snippet
#define SHOW_CATEGORY(expr) std::cout << #expr << "\t" << VALUE_CATEGORY(expr) << '\n';
int main() {
SHOW_CATEGORY(f());
SHOW_CATEGORY(rf()); // expecting prvalue, got lvalue
SHOW_CATEGORY(S{}.val);
SHOW_CATEGORY(S{}.rval); // expecting xvalue, got lvalue
}
и вывод
f() prvalue
rf() lvalue
S{}.val xvalue
S{}.rval lvalue
В обоих случаях, когда выражение связано со ссылкой, я получаю неожиданный lvalue
.
Правильный ли фрагмент и почему в данном случае? Неправильно ли это, тогда какая категория ценностей будет правильной (и почему)?
Обратите внимание: https://timsong-cpp.github.io/cppwp/n4861/basic.lval#4.4 Я понимаю, что S{}.rval
не является xvalue
, хотя мне хотелось бы знать обоснование.
Предположим, S
определено как struct S { std::string& rval; };
и у вас есть функция S g()
. Когда вы пишете std::string s = g().rval;
, хотите ли вы s
использовать конструктор перемещения из g().rval
? Это rval
может быть ссылкой на std::string
, которое не является временным.
@user12002570 user12002570 с помощью вашей цитаты я нашел ссылку в стандарте, спасибо. Я полагаю, что разумным является то, что ссылка может «указывать» на статическую переменную, которую можно изменить?
@Oersted Также обратите внимание, что указанная вами ссылка [basic.lval#4.4] не применима к S{}.rval
, потому что rval
имеет ссылочный тип.
@ user12002570 Я не понимаю. [basic.lval#4.4] прямо говорит, что S{}.val
является xvalue
, но это S{}.rval
может быть не так? Разве я не прав. Во всяком случае, он не говорит мне, что это такое...
@RaymondChen извини, я не понимаю твоих рассуждений... Возможно, я упускаю связь между семантикой перемещения и категорией значения?
@Oersted То, что вы ищете, это Basicref. Это объясняет наблюдаемое поведение, как описано в моем ответе ниже.
Правильный ли фрагмент и почему в данном случае?
Да, как описано ниже.
Во-первых, S{}.rval
— это выражение lvalue согласно Basic.ref, которое гласит:
Если объявлено, что
E2
имеет тип «ссылка на T», то E1.E2 является lvalue типаT
.
В вашем примере rval
имеет int&
, что означает, что S{}.rval
является выражением lvalue.
Далее, вызов rf()
также является выражением lvalue. Из expr.call:
Вызов функции является lvalue, если тип результата является ссылочным типом lvalue или ссылкой rvalue на тип функции, xvalue, если тип результата является ссылкой rvalue на тип объекта, и prvalue в противном случае.
Прозрачный! Мне просто не хватает рационального, особенно для S{}.rval
.
@Oersted Я бы рекомендовал задать новый вопрос специально для обоснования. В частности, в своем вопросе вы можете спросить напрямую: «Каково рациональное выражение S{}.rval
как lvalue-выражения». В настоящее время в вашем текущем посте, кажется, есть два вопроса. Один о том, что говорит стандарт, а второй — об обосновании. Я уверен в первом вопросе о стандарте, но не в его обосновании. Поэтому обычно рекомендуется задавать один вопрос в каждом сообщении, потому что люди могут знать ответ на одну часть, но не на другую. Вы можете связать этот вопрос в своем новом вопросе.
Срок действия S{}
истекает, поэтому срок действия S{}.rval
истекает, но срок действия того, на что ссылается S{}.rval
, не истекает. По аналогии рассмотрим struct S2 { int* p; };
Тогда S2{}.p
— истекающий int*
, а *S2{}.p
— неистекающий int
.
@RaymondChen Вы можете ответить на это в недавно заданном вопросе
rf()
— это выражение lvalue. См. этот ответ в обмане, в котором говорится: «Вызов функции является lvalue, если тип результата является ссылочным типом lvalue»