Инструмент определения категории значения выражения: понимание результата при использовании ссылок

Изучая определение категорий значений, я попробовал предложенный здесь фрагмент: 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, хотя мне хотелось бы знать обоснование.

rf() — это выражение lvalue. См. этот ответ в обмане, в котором говорится: «Вызов функции является lvalue, если тип результата является ссылочным типом lvalue»
user12002570 23.07.2024 15:51

Предположим, S определено как struct S { std::string& rval; }; и у вас есть функция S g(). Когда вы пишете std::string s = g().rval;, хотите ли вы s использовать конструктор перемещения из g().rval? Это rval может быть ссылкой на std::string, которое не является временным.

Raymond Chen 23.07.2024 15:54

@user12002570 user12002570 с помощью вашей цитаты я нашел ссылку в стандарте, спасибо. Я полагаю, что разумным является то, что ссылка может «указывать» на статическую переменную, которую можно изменить?

Oersted 23.07.2024 15:56

@Oersted Также обратите внимание, что указанная вами ссылка [basic.lval#4.4] не применима к S{}.rval, потому что rval имеет ссылочный тип.

user12002570 23.07.2024 15:57

@ user12002570 Я не понимаю. [basic.lval#4.4] прямо говорит, что S{}.val является xvalue, но это S{}.rval может быть не так? Разве я не прав. Во всяком случае, он не говорит мне, что это такое...

Oersted 23.07.2024 16:00

@RaymondChen извини, я не понимаю твоих рассуждений... Возможно, я упускаю связь между семантикой перемещения и категорией значения?

Oersted 23.07.2024 16:03

@Oersted То, что вы ищете, это Basicref. Это объясняет наблюдаемое поведение, как описано в моем ответе ниже.

user12002570 23.07.2024 16:04
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
7
52
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Правильный ли фрагмент и почему в данном случае?

Да, как описано ниже.

Во-первых, 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 23.07.2024 16:07

@Oersted Я бы рекомендовал задать новый вопрос специально для обоснования. В частности, в своем вопросе вы можете спросить напрямую: «Каково рациональное выражение S{}.rval как lvalue-выражения». В настоящее время в вашем текущем посте, кажется, есть два вопроса. Один о том, что говорит стандарт, а второй — об обосновании. Я уверен в первом вопросе о стандарте, но не в его обосновании. Поэтому обычно рекомендуется задавать один вопрос в каждом сообщении, потому что люди могут знать ответ на одну часть, но не на другую. Вы можете связать этот вопрос в своем новом вопросе.

user12002570 23.07.2024 16:10

Срок действия S{} истекает, поэтому срок действия S{}.rval истекает, но срок действия того, на что ссылается S{}.rval, не истекает. По аналогии рассмотрим struct S2 { int* p; }; Тогда S2{}.p — истекающий int*, а *S2{}.p — неистекающий int.

Raymond Chen 23.07.2024 16:25

@RaymondChen Вы можете ответить на это в недавно заданном вопросе

user12002570 23.07.2024 16:26

Другие вопросы по теме