Quelle est la meilleure façon de déterminer si une expression est une rvalue ou une lvalue en C++? Probablement, ce n'est pas utile dans la pratique, mais comme j'apprends les valeurs et les valeurs, j'ai pensé que ce serait bien d'avoir une fonction is_lvalue
qui renvoie true si l'expression passée en entrée est une valeur l et false sinon.
Exemple:
std::string a("Hello");
is_lvalue(std::string()); // false
is_lvalue(a); // true
La plupart du travail est déjà fait pour vous par le stdlib, vous avez juste besoin d'un wrapper de fonction:
template <typename T>
constexpr bool is_lvalue(T&&) {
return std::is_lvalue_reference<T>{};
}
dans le cas où vous passez un std::string
lvalue alors T
déduira à std::string&
ou const std::string&
, pour rvalues, il en déduira std::string
Notez que la réponse de Yakk renverra un type différent, ce qui permet plus de flexibilité et vous devriez lire cette réponse et probablement l'utiliser à la place.
J'ai résolu la question ci-dessus en utilisant deux fonctions de modèle surchargées. Le premier prend en entrée une référence à une valeur l et renvoie true
. Alors que la deuxième fonction utilise une référence à rvalue. Ensuite, je laisse le compilateur correspondre à la fonction correcte en fonction de l'expression passée en entrée.
Code:
#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';
}
Production:
Is Lvalue ?
std::string() : 0
a : 1
a+b : 0
Je prendrais une page de boost::hana
et définissez la valeur de retour de is_lvalue
encode la valeur de son argument both comme valeur constexpr
, and comme type.
Cela vous permet de faire des choses comme l'envoi de balises sans passe-partout supplémentaire.
template<class T>
constexpr std::is_lvalue_reference<T&&>
is_lvalue(T&&){return {};}
le corps de cette fonction ne fait rien et la valeur du paramètre est ignorée. Cela lui permet d'être constexpr même sur des valeurs non constexpr.
Un avantage de cette technique peut être vu ici:
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 ) );
Non seulement la valeur de retour de is_lvalue
disponible dans un contexte constexpr
(comme true_type
et false_type
avoir un constexpr operator bool
), mais nous pouvons facilement choisir une surcharge en fonction de son état.
Un autre avantage est que le compilateur a du mal à pas intégrer le résultat. Avec une valeur constexpr
, le compilateur peut "facilement" oublier qu'il s'agit d'une vraie constante; avec un type, il doit d'abord être converti en bool
pour éviter qu'il ne se produise.
Utilisation std::is_lvalue_reference
et std::is_rvalue_reference
.
Vous n'avez pas besoin d'un wrapper si vous êtes satisfait de l'utilisation 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, vous pourrez utiliser les éléments suivants:
std::string a("Hello");
std::is_lvalue_reference_v<decltype((std::string()))>; // false
std::is_lvalue_reference_v<decltype((a))>; // true
Ou vous pouvez écrire un wrapper comme le suggère @Ryan Haining, assurez-vous simplement que les types sont corrects.