Как определить программно, если выражение имеет значение rvalue или lvalue в С++?

Какой лучший способ определить, является ли выражение rvalue или lvalue в С++? Вероятно, это не полезно на практике, но поскольку я изучаю rvalues ​​и lvalues, я думал, что было бы неплохо иметь функцию is_lvalue, которая возвращает true, если выражение, переданное во вводе, является lvalue и false в противном случае.

Пример:

std::string a("Hello");
is_lvalue(std::string()); // false
is_lvalue(a); // true  

Ответ 1

Большая часть работы уже выполнена для вас stdlib, вам просто нужна функция-обёртка:

template <typename T>
constexpr bool is_lvalue(T&&) {
  return std::is_lvalue_reference<T>{};
}

если вы передадите значение std::string, то T выведет значение std::string& или const std::string&, а для значений r - std::string

Обратите внимание, что ответ Yakk вернет другой тип, что обеспечивает большую гибкость, и вы должны прочитать этот ответ и, вероятно, использовать его вместо этого.

Ответ 2

Я решил вышеупомянутый вопрос, используя две перегруженные функции шаблона. Первый принимает в качестве ссылки ссылку на lvalue и возвращает true. В то время как вторая функция использует ссылку на rvalue. Затем я позволяю компилятору соответствовать правильной функции в зависимости от выражения, переданного как вход.

код:

#include <iostream>

template <typename T>
constexpr bool is_lvalue(T&) {
    return true;
}

template <typename T>
constexpr bool is_lvalue(T&&) {
    return false;
}

int main()
{
    std::string a = std::string("Hello");
    std::cout << "Is lValue ? " << '\n';
    std::cout << "std::string() : " << is_lvalue(std::string()) << '\n';
    std::cout << "a : " << is_lvalue(a) << '\n';
    std::cout << "a+b : " << is_lvalue(a+ std::string(" world!!! ")) << '\n';
} 

Вывод:

Is Lvalue ? 
std::string() : 0
a : 1
a+b : 0

Ответ 3

Я бы взял страницу из boost::hana и сделал возвращаемое значение is_lvalue закодировал lvalue-ness своего аргумента как как значение constexpr, так и как тип.

Это позволяет вам делать что-то вроде отправки меток без дополнительного шаблона.

template<class T>
constexpr std::is_lvalue_reference<T&&>
is_lvalue(T&&){return {};}

тело этой функции ничего не делает, а значение параметра игнорируется. Это позволяет ему быть constexpr даже по значениям, отличным от constexpr.

Преимущество этой техники можно увидеть здесь:

void tag_dispatch( std::true_type ) {
  std::cout << "true_type!\n";
}
void tag_dispatch( std::false_type ) {
  std::cout << "not true, not true, shame on you\n";
}

tag_dispatch( is_lvalue( 3 ) );

Не только возвращаемое значение is_lvalue доступно в контексте constexpr (поскольку true_type и false_type имеют constexpr operator bool), но мы можем легко выбрать перегрузку в зависимости от его состояния.

Другим преимуществом является то, что компилятор затрудняет не встраивание результата. При значении constexpr компилятор может "легко" забыть, что это истинная константа; с типом, он должен быть сначала преобразован в bool для возможности его забывания.

Ответ 4

Используйте std::is_lvalue_reference и std::is_rvalue_reference.

Вам не нужна оболочка, если вы довольны использованием decltype.

std::string a("Hello");
std::is_lvalue_reference<decltype((std::string()))>::value; // false
std::is_lvalue_reference<decltype((a))>::value; // true

В С++ 17 вы сможете использовать следующее:

std::string a("Hello");
std::is_lvalue_reference_v<decltype((std::string()))>; // false
std::is_lvalue_reference_v<decltype((a))>; // true

Или вы можете написать обертку, как предлагает @Ryan Haining, просто убедитесь, что вы правильно задали типы.