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

difference between using a template function type and std::function

我很好奇为什么 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);
}
B b2(f);

B不是class,也不是类型,而是模板。 C++ 中的对象声明基本上是一个声明:这里是一个类型,这里是该类型(或派生类型)的一个或多个对象。模板不是类型。模板实例化是一种类型。

std::vector b2;

这不会编译,原因与您的代码无法编译的原因相同。你必须指定模板参数来实例化一个类型,例如:

std::vector<int> b2;

同样的原因解释了你的编译错误。

话虽如此,只要您的编译器支持 C++17,对您的模板 的小改动将 使其编译:

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

    F f;
};

C++17 编译器将能够推导出 C++17 中的模板参数 due to class template deduction guides。而且,有可能通过一些额外的工作(我没有研究它)可能 fiddle 一些事情并使你的原始模板也可以在 C++17 中工作。

A(std::function<void(int)>&& func)

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

B(F&& func)

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

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

从该构造函数生成的推导指南只接受右值,并从中推导出 F。因为 fmain 中的本地 lambda)是左值,它不能绑定到右值引用,所以 CTAD 不能成功。事实上 std::move(f) 将使 b2 合式。

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

B(F const& func) : f(func) {}

现在生成了两个推导指南,每个值类别一个。