assignment - ¿Cómo determinar programáticamente si una expresión es rvalue o lvalue en C++?
lvalue required as left operand of assignment (4)
¿Cuál es la mejor manera de determinar si una expresión es un valor r o valor l en C ++? Probablemente, esto no es útil en la práctica, pero dado que estoy aprendiendo valores y valores, pensé que sería bueno tener una función is_lvalue
que devuelve verdadero si la expresión que se pasa en la entrada es un valor l y falsa en caso contrario.
Ejemplo:
std::string a("Hello");
is_lvalue(std::string()); // false
is_lvalue(a); // true
La mayor parte del trabajo ya está hecho para ti por stdlib, solo necesitas un contenedor de funciones:
template <typename T>
constexpr bool is_lvalue(T&&) {
return std::is_lvalue_reference<T>{};
}
en el caso de que pase un std::string
lvalue, T
deducirá std::string&
o const std::string&
, para rvalues deducirá std::string
Resolví la pregunta anterior usando dos funciones de plantilla sobrecargadas. El primero toma como entrada una referencia a un valor l y devuelve true
. Mientras que la segunda función usa una referencia a rvalue. Luego dejo que el compilador coincida con la función correcta dependiendo de la expresión pasada como entrada.
Código:
#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'';
}
Salida:
Is Lvalue ?
std::string() : 0
a : 1
a+b : 0
Tomaría una página de boost::hana
y haré que el valor de retorno de is_lvalue
codifique el valor is_lvalue
de su argumento como un valor constexpr
y como un tipo.
Esto le permite hacer cosas como el despacho de etiquetas sin una repetición adicional.
template<class T>
constexpr std::is_lvalue_reference<T&&>
is_lvalue(T&&){return {};}
el cuerpo de esta función no hace nada, y el valor del parámetro se ignora. Esto permite que sea uniforme incluso en valores no constexpr.
Una ventaja de esta técnica se puede ver aquí:
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 ) );
No solo está disponible el valor de retorno de is_lvalue
en un contexto constexpr
(como true_type
y false_type
tienen un constexpr operator bool
), sino que podemos elegir fácilmente una sobrecarga en función de su estado.
Otra ventaja es que dificulta al compilador no alinear el resultado. Con un valor constexpr
, el compilador puede ''fácilmente'' olvidar que es una constante verdadera; con un tipo, primero tiene que convertirse a bool
para que se olvide suceda.
Use std::is_lvalue_reference
y std::is_rvalue_reference
.
No necesita un contenedor si está contento con el uso de decltype.
std::string a("Hello");
std::is_lvalue_reference<decltype((std::string()))>::value; // false
std::is_lvalue_reference<decltype((a))>::value; // true
En C ++ 17, podrás usar lo siguiente:
std::string a("Hello");
std::is_lvalue_reference_v<decltype((std::string()))>; // false
std::is_lvalue_reference_v<decltype((a))>; // true
O podría escribir un contenedor como lo sugiere @Ryan Haining, solo asegúrese de obtener los tipos correctos.