使用模板函数类型和 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
。因为 f
(main
中的本地 lambda)是左值,它不能绑定到右值引用,所以 CTAD 不能成功。事实上 std::move(f)
将使 b2
合式。
如果你也想接受参数的左值,你可以添加另一个构造函数
B(F const& func) : f(func) {}
现在生成了两个推导指南,每个值类别一个。
我很好奇为什么 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
。因为 f
(main
中的本地 lambda)是左值,它不能绑定到右值引用,所以 CTAD 不能成功。事实上 std::move(f)
将使 b2
合式。
如果你也想接受参数的左值,你可以添加另一个构造函数
B(F const& func) : f(func) {}
现在生成了两个推导指南,每个值类别一个。