在C ++中移动const和重载的Universal Reference

时间:2013-11-18 20:38:49

标签: c++ c++11

我正在观看Scott Meyer的视频“The Universal Reference / Overloading Collision Conundrum”,他举了一个不做的例子:

class MessedUp {
public:
    template<typename T>
    void doWork(const T& param) { std::cout << "doWork(const T& param)" << std::endl; }

    template<typename T>
    void doWork(T&& param) { std::cout << "doWork(T&& param)" << std::endl; }

};

.... //somewhere in the main

MessedUp m;
int w = 10;
const int cw = 20;

m.doWork(cw); // calls doWork(const T& param) as expected 
m.doWork(std::move(cw)); // Calls doWork(T&& param)

我很好奇为什么编译器在模板重载解析期间选择了doWork(T&& param)而不是doWork(const T& param)。据我所知,const对象无法移动。

4 个答案:

答案 0 :(得分:4)

&&并不意味着移动,它意味着右值参考。 rvalue引用只会绑定到临时(匿名)对象,或者被std::movestd::forward等函数强制转换为临时对象的对象,或者编译器自动标记为临时对象的对象,如同从在简单的return X;行上运行。

您可以对const对象进行右值引用。发生这种情况时,您无法移动(除非可以移动mutable状态),但它仍然是右值参考。

现在,如果T&&是左值引用,T可以绑定到左值引用,因此在类型推导中,上下文T&&可以称为通用引用。因此,上述设计的一个真正问题是m.doWork(w)也会T&&T=int&绑定。

在重载决策中,如果其他条件相同,那么template<typename T> void foo(T&&)T=foo&匹配的函数与template<typename T> void foo(T&)的{​​{1}}相比会更差;但是在在您的情况下,没有T=fooTfoo(T const&)

答案 1 :(得分:4)

模板类型扣除和替换后,两个重载变为:

//template<typename T>
void doWork(const int& param) { std::cout << "doWork(const T& param)" << std::endl; }

//template<typename T>
void doWork(const int&& param) { std::cout << "doWork(T&& param)" << std::endl; }

请注意第二次重载中的T如何推断为const int

现在,正常的重载解析会发生什么:我们比较将参数表达式std::move(cw)(类型为const int的xvalue)转换为参数类型所需的隐式转换序列。两者都排名为Exact Matches,因此我们必须查看[over.ics.rank] / 3中的断路器并比较两个隐式转换序列S1S2(这里的引用绑定是转换序列):

  

标准转换序列S1是比标准转换序列S2更好的转换序列   [...]

     
      
  • S1S2是引用绑定[...],S1将右值引用绑定到右值,S2绑定左值引用。
  •   

由于xvalue rvalue(和glvalue),此点适用,并选择第二个重载。

答案 2 :(得分:1)

&&可用于确定非右值的临时右值。所以,你可以安全地窃取资源。

使用std::move时,它会将类型转换为右值,编译器将使用&&重载。

答案 3 :(得分:1)

正在发生的事情是使用doWork(T&& param)调用T = const int,因为这是一个完美的匹配(而不是转换为左值)。

如果您已经修改移动对象,它确实会失败,因为const对象无法移动。