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