c ++ rvalue引用和移动语义

时间:2013-02-28 15:15:59

标签: c++ c++11 move-semantics

所以我昨天在youtube上观看了c ++视频,遇到了一个关于C ++ - 11 rvalue引用和移动语义的视频。我认为我从广义上理解这个概念,但是今天当我通过TA进行我的代码时,他问我为什么在下面的代码中没有引用(如std::pair<HostName, IPAddress>& p)。在这种情况下,我根本没有考虑过这个问题,但当他问我记得视频时说“在C ++ - 11中你通常应该使用按值传递。”

我的问题是:在下面的代码中,std::pair<HostName, IPAddress> p会不会像std::pair<HostName, IPAddress>& p那样更好?是否会使用移动语义,它会有所作为吗?

IPAddress NameServer::lookup( const HostName& host ) const {
    auto it = std::find_if( vec.begin(), vec.end(),
                       [host] ( std::pair<HostName, IPAddress> p ) {
        return p.first == host;
    } );
    ...
}

1 个答案:

答案 0 :(得分:10)

在这种情况下,您应该通过const引用。当您最终想要生成传递值的副本或移动时,按值传递是有意义的;如果你不想复制或移动,特别是如果你想仅观察,你应该通过(const)引用。

这里,你的lambda谓词不需要生成它在输入中接收的对的任何副本:因此,没有理由按值传递(也不能通过vaue 捕获)。

IPAddress NameServer::lookup( const HostName& host ) const {
    auto it = std::find_if( vec.begin(), vec.end(),
        [&host] ( std::pair<HostName, IPAddress> const& p ) {
    //   ^^^^^                                   ^^^^^^
        return p.first == host;
    } );
    ...
}

相反,请考虑这种情况(典型的C ++ 03代码):

struct A
{
    A(string const& s) : _s(s) { }
private:
    string _s;
};

在C ++ 11中,由于你有移动语义,而不是通过常量引用传递s,你可以简单地传递值并将其移动到成员变量中:

struct A
{
    A(string s) : _s(move(s)) { }
private:
    string _s;
};

这是有道理的,因为我们总是最终生成传递的值的副本。

正如Benjamin Lindley在评论中正确指出的那样,如果您可以接受,可以编写上述构造函数的重载,这些构造函数通过引用获取其参数:

struct A
{
    A(string const& s) : _s(s) { }    // 1 copy
    A(string&& s) : _s(move(s)) { }   // 1 move
private:
    string _s;
};

上述版本只允许为左值执行一个副本,为rvalues执行一个副本,而通过值传递的版本始终执行一个额外的移动。因此,如果移动对于您的参数类型来说是一项昂贵的操作(string不是这种情况,那么这种解决方案可能更为可取,但其他类型可能就是这种情况)。

但是,如果您的函数使用几个参数,那么这样做可能会很麻烦。为了减少工作量,您可以编写一个单独的函数模板,该模板采用通用引用并完善转发其参数。 This Q&A on StackOverflow与该主题相关。