标准算法的 lambda 应该接受什么样的参数?

问题描述 投票:0回答:1

所以我昨天在 youtube 上观看 C++ 视频,发现了一个关于 C++-11 右值引用和移动语义的视频。我想我从广义上理解了这个概念,但是今天当我与助教一起检查我的代码时,他问我为什么不在下面的代码中提供参考(如

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 lambda move-semantics
1个回答
10
投票

在这种情况下,您应该通过

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

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

上述版本只允许对左值执行一份副本,对右值执行一次移动,而按值传递的版本始终执行一次额外的移动。因此,如果移动对于您的参数类型来说是一项昂贵的操作(这不是

string
的情况,但可能是其他类型的情况),则此解决方案可能更可取。

但是,如果您的函数需要多个参数,那么这样做可能会很麻烦。为了减少工作量,您可以编写一个函数模板,该模板采用“通用引用”并完美转发其参数。 StackOverflow 上的问答与主题相关。

© www.soinside.com 2019 - 2024. All rights reserved.