Как проверить, является ли выражение временным?
Со следующим макросом:
#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");