2017-01-04 1 views
1

Voici un code à VS2015:Pourquoi l'argument de référence rvalue préfère-t-il la référence const lvalue au paramètre de référence rvalue?

class Elem 
{ 
public: 
    Elem(int i) : a(i) {} 
    Elem(Elem && other) = default; 
    Elem(const Elem & other) = default; 
private: 
    int a; 
}; 

int main() 
{ 
    std::vector<Elem> vv; 

    // case 0 
    vv.push_back(Elem{ 0 }); // call push_back(Elem &&); 

    // case 1 
    Elem e1{ 1 }; 
    vv.push_back(e1); // call push_back(const Elem &); 

    // case 2 
    Elem e2{ 2 }; 
    auto & lref = e2; 
    vv.push_back(lref); // call push_back(const Elem &); 

    // case 3 
    Elem e3{ 3 }; 
    auto && rref = std::move(e3); 
    vv.push_back(rref); // call push_back(const Elem &); 

    // case 4 
    Elem e4{ 4 }; 
    vv.push_back(std::move(e4)); // call push_back(Elem &&); 

    return 0; 
} 

Dans le cas 3, le type de rref est référence rvalue et sa catégorie de valeur est lvalue et demande push_back(const Elem &).

Dans le cas 4, Selon moderne et efficace C++ Point 23, une implémentation de std::move est quelque chose comme

// C++ 14  
template<typename T> 
decltype(auto) move(T&& param) 
{ 
    using ReturnType = remove_reference_t<T>&&; 
    return static_cast<ReturnType>(param); 
} 

Le type de std::move(e4) est Elem && et sa catégorie de valeur est prvalue, appelle push_back(Elem &&).

Ainsi, un lvalue de T&& correspond const T &, et un prvalue de T&& matchs T&&, qu'est-ce que le type et la catégorie de valeur d'une expression font réellement pendant la résolution de surcharge entre T, const T & et T &&?


Désolé de ne pas avoir décrit clairement mon problème. Comme tant de liens ont dit que, si la catégorie de valeur d'un argument est prvalue, la fonction avec T&& sera appelée; la catégorie de valeur est lvalue, la fonction avec const T & sera appelée. Puis-je simplement dire que le type de l'argument est utilisé pour la résolution de surcharge, alors que la catégorie de valeur est vérifiée pour la liaison de référence lorsque le type du paramètre est une référence?

+5

Dès que votre variable a un nom, c'est une valeur –

+0

http://stackoverflow.com/a/18766736/390913 – perreal

Répondre

0

Le moment où vous affectez un nom de variable (dans ce cas "e3"), et utilisez cette variable pour passer dans la fonction, il devient lvalue. C'est pourquoi il appelle la version lvalue de la fonction "push_back". Mais quand vous passez comme "std :: move (e4)" dans le cas 4, il est passé comme rvalue et c'est pourquoi il appelle la version de référence rvalue de la fonction "push_back". Juste en passant par le web, j'ai trouvé ce lien qui explique la référence rvalue en termes presque similaires. Vous pouvez vérifier ce lien.

http://simpletechtalks.com/rvalue-references/

1

rref est une lvalue car il a un nom, donc push_back(const Elem &) est appelé. Le move dans une variable nommée est inutile, mais si vous insistez, vous pouvez changer votre code:

// case 3 
Elem e3{ 3 }; 
auto && rref = std::move(e3); 
vv.push_back(std::forward<decltype(e3)>(rref)); 

Et puis push_back(Elem &&) est appelé. Ou faites juste ce que vous avez fait dans le cas 4.