c++ - std::function - 值与引用参数

标签 c++ c++11

std::function 之间是否有任何实际差异?对于具有值参数的类型与对值参数的常量引用?考虑以下代码:

auto foo = [] (VeryBigType i) {     
};

auto bar = [] (const VeryBigType& i) {
};

std::function<void(VeryBigType)> a;
a = foo;
a = bar;

std::function<void(const VeryBigType&)> b;
b = foo;
b = bar;

这段代码编译没有问题并且运行良好。我知道按值传递与按引用传递具有性能差异,因此 foobar会表现不同。但是是否有任何差异取决于 std::function模板类型?例如,两者之间是否存在任何实现和/或行为和/或性能差异?std::function<void(VeryBigType)>(bar)对比 std::function<void(const VeryBigType&)>(bar)或者这些结构是等效的?

最佳答案

cppreference says那个std::function<R(Args...)>::operator()有签名

R operator()(Args... args) const;

并且它调用存储的可调用 f基本上由 f(std::forward<Args>(args)...) .性能特征取决于模板参数和 lambda 的参数类型,我认为看到可能发生的一切会很有帮助。就您而言,您有 2 std::function参数的类型、2 个可调用对象和 3 个可能的值类别,为您提供 12 种可能性。
  • std::function<void(VeryBigType)> f = [](VeryBigType i) { }
  • 如果你用左值调用它,比如
    VeryBigType v;
    f(v);
    

    这将复制 v进入 operator() 的论点,然后 operator()将右值传递给 lambda,它将将该值移动到 i .总成本:1 份 + 1 次移动
  • 如果你用纯右值调用它,比如
    f(VeryBigType{});
    

    然后这会将纯右值具体化为 operator() 的参数,然后将右值传递给 lambda,它将移动到 i .总成本:1 次移动
  • 如果你用 xvalue 调用它,比如
    VeryBigType v;
    f(std::move(v));
    

    这将移动 v进入 operator() 的论点,它将右值传递给 lambda,它将再次将其移动到 i .总成本:2 次移动。
  • std::function<void(VeryBigType)> f = [](VeryBigType const &i) { }
  • 如果你用左值调用它,这将复制一次到 operator() 的参数中。 ,然后 lambda 将获得对该参数的引用。总成本:1 份。
  • 如果你用纯右值调用它,这会将它具体化为 operator() 的参数。 ,它将对该参数的引用传递给 lambda。总费用:无。
  • 如果你用 xvalue 调用它,这会将它移动到 operator() 的参数中。 ,它将对该参数的引用传递给 lambda。总成本:1 次移动。
  • std::function<void(VeryBigType const&)> f = [](VeryBigType i) { }
  • 如果你用左值或 xvalue(即用左值)调用它,operator()将收到对它的引用。如果你用纯右值调用它,它将被物化为一个临时的,并且 operator()将收到对此的引用。无论如何,对 lambda 的内部调用将始终复制。总成本:1 份。
  • std::function<void(VeryBigType const&)> f = [](VeryBigType const &i) { }
  • 再说一次,不管你怎么称呼它,operator()将只收到对它的引用,而 lambda 将只收到相同的引用。总费用:无。

  • 那么,我们学到了什么?如果两者都std::function并且 lambda 引用,您可以避免任何无关的拷贝和移动。尽可能使用它。将按值 lambda 放入 by- const -左值引用std::function ,但是,这是一个坏主意(除非您必须这样做)。本质上,左值引用“忘记”了参数的值类别,并且始终复制到 lambda 的参数。放一个by- const -lvalue-reference lambda 内的按值 std::function在性能方面非常好,但只有在调用其他需要按值的代码时才需要这样做 std::function , 因为否则是一个引用 std::function达到同样的目的,但复制和移动更少。将按值 lambda 放入按值 std::function比放一个by-const稍差-lvalue-reference lambda 在其中,由于所有调用中的额外移动。最好是通过右值引用来获取 lambda 的参数,这与通过 - const 获取它几乎相同。 -lvalue-reference 除非您仍然可以改变参数,就像您无论如何都按值获取它一样。

    TL;DR:std::function 中的按值和右值引用参数模板参数应该对应于 by-rvalue-reference 或 by- const -lvalue-reference 参数在您放入 std::function 的 lambda 中.类型中的左值引用参数应该对应于 lambda 中的左值引用参数。其他任何东西都会导致额外的拷贝或移动,并且应该只在需要时使用。

    关于c++ - std::function - 值与引用参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61662210/

    相关文章:

    c++ - 复制 std::vector 但将 lambda 应用于每个元素

    c++ - 非内联构造函数中的默认参数

    c++ - istream_iterator 初始化 vector

    c++ - 引用成员函数?

    c++ - 诺基亚 lumia 1020 USB 大容量存储访问 C++

    c++ - 以自然(非反向)顺序将函数应用于 std::tuple 中的元素

    c++ - 矩阵循环移位

    c++ - 是否有将 "override"标识符添加到现有 C++ 代码的工具

    c++ - weak_ptr C++ 中的比较运算符

    c++ - unique_ptr<T>.get() 方法在使用原始指针分配时调用析构函数?