成员函数引用限定返回类型

时间:2014-07-14 13:04:38

标签: c++ c++11

阅读迈耶斯的新书我发现了一些非常相似的东西:

// compile with
// g++-4.8 --std=c++11 -Wall   main3.cc && ./a.out
#include <iostream>
#include <vector>

class Widget
{
    public:
        using DType = std::vector<int>;


        DType& data() &  // lvalue
          { 
            std::cout << "data (lvalue)    : " << &data_[0] << std::endl; 
            return data_; 
          };

        DType data() &&  //  rvalue
          { 
            std::cout << "data (rvalue)    : " << &data_[0] << std::endl; 
            return std::move(data_);  
          }; 

        // Please Note
        // int parameter is here to make the overloading possible
        // in a single class
        DType&& data(int) && 
          { 
            std::cout << "data (rvalue ref): " << &data_[0] << std::endl; 
            return std::move(data_);  
          }; 
    private:
        DType data_ { 0 };
};

Widget getWidget() { return Widget(); }


int main(int argc, char *argv[])
{
    Widget w1;
    std::vector<int> d1 = w1.data();
    std::cout << "d1 copied        : " << &d1[0] << std::endl;

    std::vector<int> d2 = getWidget().data();
    std::cout << "d2 moved         : " << &d2[0] << std::endl;

    std::vector<int> d3 = getWidget().data(0);
    std::cout << "d3 moved         : " << &d3[0] << std::endl;

    return 0;
}

我的观点非常简单: 在我的盒子上,我希望我有这些结果

data (lvalue)    : 0x8e28008
d1 copied        : 0x8e28018
data (rvalue)    : 0x8e28028
d2 moved         : 0x8e28028
data (rvalue ref): 0x8e28038
d3 moved         : 0x8e28038

因此,第一个向量被复制,而第二个和第三个被移动。

您可以使用两种不同的签名来实现移动操作:

一个人返回右值

DType data() &&  //  rvalue

和一个退回右值参考

DType&& data() && 

他们取得了同样的结果:我看不到任何差异吗?什么是“最好的”?

1 个答案:

答案 0 :(得分:2)

  

您可以使用两种不同的签名来实现移动操作:

这是错误的。

第一个签名,即返回DType的签名,执行返回值的移动。第二个签名,即返回DType&&的签名只返回一个引用。它没有移动任何东西。

移动发生在其他代码中,特别是std::vector<int> d3 =的部分。从xvalue初始化矢量会执行移动。这就是行动,而不是功能。 然而,其他类型的操作不会执行移动:

// no move, just binding the member to a reference
std::vector<int>&& d3 = getWidget().data(0);

然而,使用第一个函数,移动始终发生:

// move into a temporary, and bind *that* to a reference
std::vector<int>&& d2 = getWidget().data();

第二个签名很危险。很容易意外地将引用返回到临时。很容易写出误导性的客户端代码,你认为某些东西已被移出但没有。返回右值引用有一个明智的用例,并且标准库已经以std::movestd::forward的形式处理了该用例。