c++ - 您应该通过 std::function 还是通过通用引用将可调用对象作为接收器参数传递?

标签 c++ std-function forwarding-reference

考虑以下代码:

#include <functional>
#include <iostream>

struct S1
{
    S1(const std::function<void()>& _func) : func{_func} {}

    std::function<void()> func;
};

struct S2
{
    template<typename F>
    S2(F&& _func) : func{std::forward<F>(_func)} {}

    std::function<void()> func;
};

int main()
{
    S1 s1{[](){ std::cout << "S1 function called\n"; }};
    S2 s2{[](){ std::cout << "S2 function called\n"; }};
    
    s1.func();
    s2.func();

    return 0;
}

我试图了解这两个构造函数实现的设计优点/缺点是什么。 另外,两者之间是否存在性能差异。

我的主要问题是,当知道可调用对象的签名并将其存储为类中的 std::function 时,是否应该接受 std::function 对象或对可调用对象的通用引用?

据我所知,通用引用是首选方法,因为它为编译器提供了进行内联的最佳机会。

但是,我可以想到使用通用引用的一些缺点:

  • 它可能与采用相同数量参数的其他构造函数发生冲突,因此应使用 SFINAE 或 C++20 概念来保护它。 (这是一个库问题,所以没什么大不了的)
  • 对于它期望的可调用类型不太明确(也可以使用概念进行约束)。 (这可以认为是用户问题)
  • 由于前面的缺点,不适合可调用的情况下的错误点从调用站点移至成员初始化站点,这更难理解。 (这绝对是用户问题)

我想我真正的问题是: 您是否应该在构造函数接口(interface)中公开类的 std::function 性质,或者这是必须保持隐藏的实现细节?

最佳答案

第一个示例将始终复制,即使可以避免,因此它不是最佳的。

第二个示例有您提到的缺点。

推荐的方式是按值获取并移动:

struct S3
{
    S3(std::function<void()> _func) : func{std::move(_func)} {}

    std::function<void()> func;
};

现在这可能看起来违反直觉,因为您按值获取复制成本可能很高的对象。但这不是问题。我们来分析一下可能的情况:

  • 调用者无法给您可移动的对象。在这种情况下,拷贝是不可避免的。这会导致复制后移动:

    auto l = []() {};
    S3 s4{l};
    l(); 
    
  • 调用者给你一个 xvalue。在这种情况下,涉及两个 Action :

    auto l = []() {};
    S3 s4{std::move(l)};
    // no more uses of l
    
  • 调用者给你一个纯右值。这会导致 1 或 2 次移动。从 C++17 开始,使用新的临时具体化规则,保证您只需一步。

    S3 s4{[]() {}};
    

正如你所看到的,即使你按值取值,你也不会做任何不必要的复制操作,前提是 Action 便宜。

关于c++ - 您应该通过 std::function 还是通过通用引用将可调用对象作为接收器参数传递?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70328394/

相关文章:

c++ - C++ 标准是否允许这种浮点行为?

c++ - 查找表与运行时计算效率 - C++

c++ - 如何将可变参数传递给虚函数

c++ - 将非静态成员函数作为 std::function 传递

c++ - 为通用引用和指向成员的指针推导出冲突类型

c++ - X3 : Is this parser, *(char - eol),消耗所有行?

c++ - 为什么 std::get 不适用于变量?

c++ - 将N-arg函数包装到另一个函数中

c++ - 错误 C2783 : '_Ty &&std::forward(remove_reference<_Ty>::type &&) throw()' : could not deduce template argument for '_Ty'

c++ - 如何将类型约束和隐式转换与 C++11 通用引用相结合?