Как проверить, является ли выражение временным?

Со следующим макросом:

#define ASSERT_IF_TEMP(expr) static_assert(?, "Is temporary!");

Что я должен поставить для вопросительного знака?

Ответ 1

Сначала мы должны уточнить: что вы подразумеваете под "временным"?

Многие люди имеют в виду разные вещи, когда говорят временные. Технически int() не является временным, но большинство людей включит их в свое значение этого термина. Технически, учитывая std::string s;, тогда move(s) также не является временным, но вы можете рассматривать его как одно с вашим макросом.

Первый вид "временных", о которых я упоминал выше, действительно является "выражениями prvalue". Это те вещи std::string("foo") или int(), но не move(s), а также (точно), а не s вещи. Оператор decltype дает не ссылочный тип для первого типа "временных", о которых я говорил выше. Для второго рода move(s), которые являются значениями x, он даст ссылку rvalue. А для "не временных", т.е. Случаев s, он даст ссылку lvalue.

Итак, чтобы подвести итог, я определяю три точных макроса, и вы можете выбрать из них

#define IS_LVALUE(...) std::is_lvalue_reference<decltype((__VA_ARGS__))>::value
#define IS_XVALUE(...) std::is_rvalue_reference<decltype((__VA_ARGS__))>::value
#define IS_PRVALUE(...) !std::is_reference<decltype((__VA_ARGS__))>::value

Ответ 2

ИЗМЕНИТЬ

Я понял, что мой подход делает то же самое, что и код, который вы сказали, не работает, только логически инвертирован:

std::is_lvalue_reference<decltype((expr))>::value

Не могли бы вы рассказать о том, в какой ситуации он работает против ваших ожиданий?


Вы можете использовать правила сбрасывания ссылок, например:

std::is_rvalue_reference<decltype((expr))&&>::value

Если expr является lvalue некоторого (возможно, const) типа T, decltype((expr)) будет разрешено T&, а T& && скроется до T&.

В противном случае, если expr является значением x некоторого типа T, decltype((expr)) будет T&&, а T&& && уменьшится до всего T&&.

В противном случае expr будет prvalue некоторого типа T, decltype((expr)) даст T, и, таким образом, весь тип будет T&&.

Примеры:

template <typename T>
struct is_rvalue : std::is_rvalue_reference<T&&>
{};

struct x {};
x a; const x b{};

static_assert(is_rvalue<decltype((x()))>::value, "x() is an rvalue");
static_assert(!is_rvalue<decltype((a))>::value, "a is an lvalue");
static_assert(!is_rvalue<decltype((b))>::value, "b is an lvalue");
static_assert(is_rvalue<decltype((std::move(a))>::value, "std::move(a) is an rvalue");