使用模板函数类型和 std::function 之间的区别

Bar*_*uch 0 c++ c++17

我很好奇为什么a2可以,但b2无法编译:

#include <functional>
#include <iostream>

class A {
public:
    A(std::function<void(int)>&& func) : f(std::move(func)) {}

    std::function<void(int)> f;
};

template <class F>
class B {
public:
    B(F&& func) : f(std::move(func)) {}

    F f;
};

int main() {
    int a = 1;
    auto f = [a](int b){std::cout << a+b << '\n';};
    A a1([a](int b){std::cout << a+b << '\n';});
    A a2(f);
    B b1([a](int b){std::cout << a+b << '\n';});
    // B b2(f);
    a1.f(2);
    a2.f(2);
    b1.f(2);
    // b2.f(2);
    f(2);
}
Run Code Online (Sandbox Code Playgroud)

Sto*_*ica 6

A(std::function<void(int)>&& func)
Run Code Online (Sandbox Code Playgroud)

A可以用std::function右值初始化。现在,f(in main)不是a std::function,因为每个 lambda 都有其自己不同的类型。但是我们可以创建一个临时std::function的,并将右值引用绑定func到它。

B(F&& func)
Run Code Online (Sandbox Code Playgroud)

不要让外表欺骗了你。这可能看起来像转发参考,但事实并非如此。从语法上讲,转发引用是对模板参数的右值引用,但它必须是我们要转发到的函数的模板参数。

的构造函数B不是模板,因此func不是转发引用。

从该构造函数生成的推导指南仅接受右值,并F从中进行推导。因为f( 中的本地 lambda main) 是左值,所以它无法绑定到右值引用,因此 CTAD 无法成功。确实std::move(f)会使得b2格式良好。

如果您还想接受参数的左值,您可以添加另一个构造函数

B(F const& func) : f(func) {}
Run Code Online (Sandbox Code Playgroud)

现在正在生成两个演绎指南,每个值类别一个。