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

#c #c 11 #static-assert

#c #c 11 #статическое утверждение

Вопрос:

С помощью следующего макроса:

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

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

Комментарии:

1. Результат выражения всегда является временным.

2. @Clinton: Можете ли вы пояснить, почему вы хотите это сделать?

3. @Neil: Ерунда. Если у меня есть char* ptr = some_pointer(); , то выражение *ptr является значением lvalue.

4. @Neil Butterworth За некоторое определение временного. С практической точки зрения, временное соответствует тому, что стандарт называет rvalue , а результатом некоторых выражений являются lvalues .

5. вы могли бы утверждать, что что-то является временным, если находится в стеке (особенно смещение), а не в куче?

Ответ №1:

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

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

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

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

 #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
  

Комментарии:

1. Спасибо за очень подробный ответ. На самом деле я ищу prvalues , поскольку у этого могут быть удалены копии, если обернуть их в лямбда-выражения (следовательно, отложить их оценку) перед передачей их функциям. Делать это для других вещей, я считаю, пустая трата времени.

2. Есть ли способ определить разницу между std::move(x) и std::move(X())? (последнее выигрывает от отложенной оценки).

Ответ №2:

Редактировать

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

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

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


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

 std::is_rvalue_reference<decltype((expr))amp;amp;>::value
  

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

В противном случае, если expr это xvalue некоторого типа T , decltype((expr)) будет Tamp;amp; , и Tamp;amp; amp;amp; уменьшится до просто Tamp;amp; .

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

Примеры:

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

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");