2017-01-04 3 views
1

Вот код в VS2015:Почему ссылочный аргумент rvalue предпочитает ссылку const lvalue на параметр ссылки 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; 
} 

В случае 3, тип rref является ссылка Rvalue, и его значение категория lvalue, и называет push_back(const Elem &).

В случае 4, в соответствии с действующим Современной C++ Пункт 23, реализация std::move что-то вроде

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

Тип std::move(e4) является Elem &&, и его значение категория prvalue, вызывает push_back(Elem &&).

Так lvalue из T&& матчей const T & и prvalue из T&& матчей T&&, что же тип и категория значения выражения на самом деле при разрешении перегрузки между T, const T & и T &&?


Извините, что не описал свою проблему четко. Так как многие ссылки говорят, что если категория значений аргумента равна prvalue, будет вызвана функция с T&&; значение категории lvalue, будет вызвана функция с const T &.

Могу ли я просто сказать, что тип аргумента используется для разрешения перегрузки, а категория значений проверяется для привязки ссылок, когда тип параметра является ссылкой?

+5

Как только ваша переменная имеет имя, это lvalue –

+0

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

ответ

0

Момент, когда u присваивает имя переменной (в данном случае «e3») и использует эту переменную для передачи в функции, она становится lvalue. Вот почему его вызов lvalue версии функции «push_back». Но когда u проходит как «std :: move (e4)» в случае 4, он передается как rvalue, и именно поэтому его вызывает ссылочную версию rvalue функции «push_back».

только через Интернет я нашел эту ссылку, которая объясняет ссылку rvalue в почти похожих терминах. u можете проверить эту ссылку.

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

1

rref является именующим, так как у него есть имя, так push_back(const Elem &) называется. move в имени переменной бесполезно, но если вы настаиваете на нем вы можете изменить свой код:

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

push_back(Elem &&) И тогда называется. Или просто сделайте то, что вы сделали в случае 4.