【问题标题】:c++ rvalue reference and move semanticsc++ 右值引用和移动语义
【发布时间】:2013-02-14 20:28:36
【问题描述】:

所以我昨天在 youtube 上观看 c++ 视频,偶然发现了一个关于 C++-11 右值引用和移动语义的视频。我想我从广义上理解了这个概念,但是今天当我与 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;
    } );
    ...
}

【问题讨论】:

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


    【解决方案1】:

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

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

    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 在 cmets 中正确指出的那样,如果您可以接受,您可以编写上述构造函数的重载,通过引用获取其参数:

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

    上面的版本只允许对左值执行一次复制,对右值执行一次移动,而按值传递的版本总是执行一次额外的移动。因此,如果移动对于您的参数类型而言是一项昂贵的操作(string 不是这种情况,但其他类型可能是这种情况),则此解决方案可能更可取。

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

    【讨论】:

    • @juanchopanza:是的,绝对是:忽略了这一点。谢谢
    • “没有理由通过常量引用传递 s” -- 这并不完全正确。如果您想避免编写多个执行相同操作的构造函数,那么是的,按值传递然后移动是一个不错的选择。特别是当有多个参数时,因为重复是指数的。但是,如果您对编写额外的重载感到满意,您从编写一个接受 const 引用然后复制的版本和另一个接受 r 值引用然后移动的版本中受益。跨度>
    • @BenjaminLindley:确实如此,但在这种情况下,我宁愿建议编写一个接受通用引用(并完美转发其参数)的模板版本,以避免重复。没有?
    • @Benjamin:无论如何,我进一步讨论了,谢谢你的贡献。
    • 也许吧。我对此没有太多经验,但在我看来,您可能会冒在那里调用不需要的构造函数以及神秘错误消息的风险。此外,您的代码不会像自我记录那样。
    猜你喜欢
    • 2011-06-26
    • 1970-01-01
    • 2012-12-10
    • 1970-01-01
    • 2023-04-04
    • 1970-01-01
    • 2011-11-30
    相关资源
    最近更新 更多