web-dev-qa-db-fra.com

Comment déterminer par programme si une expression est rvalue ou lvalue en C ++?

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  
49
Giuseppe Pes

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.

59
Ryan Haining

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
26
Giuseppe Pes

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.

16

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.

8
Pharap