可变参数模板、类型推导和 std::function
Variadic templates, type deduction and std::function
我正在尝试创建一个模板函数,可以将具有任何类型和数量参数的其他函数传递给它,并将其绑定到 std::function
。我设法做到了:
#include <iostream>
#include <functional>
int foo(int bar)
{
std::cout << bar << std::endl;
return bar;
}
template <typename Ret, typename... Args>
std::function<Ret (Args...)> func(std::function<Ret (Args...)> f)
{
return f;
}
int main()
{
//auto barp = func(foo); // compilation error
auto bar = func(std::function<void (int)>(foo));
bar (0); // prints 0
}
我只想调用 auto barp = func(foo);
并推导出类型,但是这一行给出了以下编译错误:
error: no matching function for call to ‘func(void (&)(int))’
auto barp = func(foo);
^
note: candidate is:
note: template<class Ret, class ... Args> std::function<_Res(_ArgTypes ...)> func(std::function<_Res(_ArgTypes ...)>)
std::function<Ret (Args...)> func(std::function<Ret (Args...)> f)
^
note: template argument deduction/substitution failed:
note: mismatched types ‘std::function<_Res(_ArgTypes ...)>’ and ‘int (*)(int)’
auto barp = func(foo);
^
为什么要尝试将 std::function<_Res(_ArgTypes ...)>
与 int (*)(int)
匹配?我觉得我应该让编译器以某种方式将 _Res(_ArgTypes ...)
扩展为 int(int)
,但是如何?
函数不是 std::function
,它可以转换为一个。
但是,您可以推导出函数的参数,除非对重载有歧义。
#include <iostream>
#include <functional>
int foo(int bar)
{
std::cout << bar << std::endl;
return 0;
}
// Will cause error.
//int foo(double);
template <typename Ret, typename... Args>
std::function<Ret (Args...)> func(Ret f(Args...))
{
return f;
}
int main()
{
auto bar = func(foo);
bar (0); // prints 0
}
你想用原来的std::function
做的和这个类似,更明显是行不通的:
template<typename T>
struct A
{
A(T);
};
template<typename T>
void func(A<T> a);
int main()
{
func(42);
}
42
不是 A
,但它可以转换为一个。但是,将其转换为一个需要 T
已知。
您的代码在语义上等同于(如果已编译):
int foo(int x){
std::cout << x << std::endl;
return x;
}
int main(){
auto bar = [](int x){ return foo(x); };
bar(0);
}
除了 return x
部分,但这只是我纠正你未定义的行为。
你的返回 std::function
的函数是非常不必要的,除了可能是为了减少输入。
您可以在没有包装函数的情况下轻松使用 std::function
s 构造函数。
但是,还是。
做你想做的事,你应该传递函数指针本身;没有不可能的转换。
试试这个:
int foo(int x){
return x + 1;
}
template<typename Ret, typename ... Args>
auto func(Ret(*fp)(Args...)) -> std::function<Ret(Args...)>{
return {fp};
}
int main(){
auto bar = func(foo);
std::cout << bar(0) << std::endl; // outputs '1'
}
您的代码不起作用的原因是当您将参数传递给 func
时试图进行隐式转换。
正如我所说,您的代码目前在语义上等同于我在上面使用 lambda 表达式显示的示例。我强烈建议在需要函数包装的地方只使用 lambda 表达式!它们更加灵活,并且是语言的核心部分,而不是库功能。
记住,非捕获 lambda 可以转换为函数指针;所以以下是符合的:
int main(){
int(*bar)(int) = [](int x){ return x + 1; };
std::cout << bar(0) << std::endl;
}
并且要在 post 中具有您想要的类似功能,我们可以这样写:
int main(){
auto func = +[](int x){ return x + 1; };
std::cout << "foo(5) = " << func(5) << std::endl;
func = [](int x){ return x * 2; };
std::cout << "bar(5) = " << func(5) << std::endl;
}
注意我们没有乱用函数指针声明或库类型吗? read/write 对每个人都好得多。 One thing to notice in this example is the unary +
operator; to perform a conversion to function pointer before assigning it to the variable. 它实际上看起来非常 实用 ,这似乎是您在这里想要实现的目标。
尝试通过 operator()
:
解开函子(lambda 和 std::function)
#include <iostream>
#include <functional>
int foo(int bar)
{
std::cout << bar << std::endl;
return 0;
}
template<typename /*Fn*/>
struct function_maker;
template<typename RTy, typename... ATy>
struct function_maker<RTy(ATy...)>
{
template<typename T>
static std::function<RTy(ATy...)> make_function(T&& fn)
{
return std::function<RTy(ATy...)>(std::forward<T>(fn));
}
};
template<typename /*Fn*/>
struct unwrap;
template<typename CTy, typename RTy, typename... ATy>
struct unwrap<RTy(CTy::*)(ATy...) const>
: function_maker<RTy(ATy...)> { };
template<typename CTy, typename RTy, typename... ATy>
struct unwrap<RTy(CTy::*)(ATy...)>
: function_maker<RTy(ATy...)> { };
template<typename T>
auto func(T f)
-> decltype(unwrap<decltype(&T::operator())>::make_function(std::declval<T>()))
{
return unwrap<decltype(&T::operator())>::make_function(std::forward<T>(f));
}
int main()
{
//auto barp = func(foo); // compilation error
auto bar = func(std::function<void(int)>(foo));
auto bar2 = func([](int)
{
// ...
});
bar(0); // prints 0
}
我正在尝试创建一个模板函数,可以将具有任何类型和数量参数的其他函数传递给它,并将其绑定到 std::function
。我设法做到了:
#include <iostream>
#include <functional>
int foo(int bar)
{
std::cout << bar << std::endl;
return bar;
}
template <typename Ret, typename... Args>
std::function<Ret (Args...)> func(std::function<Ret (Args...)> f)
{
return f;
}
int main()
{
//auto barp = func(foo); // compilation error
auto bar = func(std::function<void (int)>(foo));
bar (0); // prints 0
}
我只想调用 auto barp = func(foo);
并推导出类型,但是这一行给出了以下编译错误:
error: no matching function for call to ‘func(void (&)(int))’
auto barp = func(foo);
^
note: candidate is:
note: template<class Ret, class ... Args> std::function<_Res(_ArgTypes ...)> func(std::function<_Res(_ArgTypes ...)>)
std::function<Ret (Args...)> func(std::function<Ret (Args...)> f)
^
note: template argument deduction/substitution failed:
note: mismatched types ‘std::function<_Res(_ArgTypes ...)>’ and ‘int (*)(int)’
auto barp = func(foo);
^
为什么要尝试将 std::function<_Res(_ArgTypes ...)>
与 int (*)(int)
匹配?我觉得我应该让编译器以某种方式将 _Res(_ArgTypes ...)
扩展为 int(int)
,但是如何?
函数不是 std::function
,它可以转换为一个。
但是,您可以推导出函数的参数,除非对重载有歧义。
#include <iostream>
#include <functional>
int foo(int bar)
{
std::cout << bar << std::endl;
return 0;
}
// Will cause error.
//int foo(double);
template <typename Ret, typename... Args>
std::function<Ret (Args...)> func(Ret f(Args...))
{
return f;
}
int main()
{
auto bar = func(foo);
bar (0); // prints 0
}
你想用原来的std::function
做的和这个类似,更明显是行不通的:
template<typename T>
struct A
{
A(T);
};
template<typename T>
void func(A<T> a);
int main()
{
func(42);
}
42
不是 A
,但它可以转换为一个。但是,将其转换为一个需要 T
已知。
您的代码在语义上等同于(如果已编译):
int foo(int x){
std::cout << x << std::endl;
return x;
}
int main(){
auto bar = [](int x){ return foo(x); };
bar(0);
}
除了 return x
部分,但这只是我纠正你未定义的行为。
你的返回 std::function
的函数是非常不必要的,除了可能是为了减少输入。
您可以在没有包装函数的情况下轻松使用 std::function
s 构造函数。
但是,还是。
做你想做的事,你应该传递函数指针本身;没有不可能的转换。
试试这个:
int foo(int x){
return x + 1;
}
template<typename Ret, typename ... Args>
auto func(Ret(*fp)(Args...)) -> std::function<Ret(Args...)>{
return {fp};
}
int main(){
auto bar = func(foo);
std::cout << bar(0) << std::endl; // outputs '1'
}
您的代码不起作用的原因是当您将参数传递给 func
时试图进行隐式转换。
正如我所说,您的代码目前在语义上等同于我在上面使用 lambda 表达式显示的示例。我强烈建议在需要函数包装的地方只使用 lambda 表达式!它们更加灵活,并且是语言的核心部分,而不是库功能。
记住,非捕获 lambda 可以转换为函数指针;所以以下是符合的:
int main(){
int(*bar)(int) = [](int x){ return x + 1; };
std::cout << bar(0) << std::endl;
}
并且要在 post 中具有您想要的类似功能,我们可以这样写:
int main(){
auto func = +[](int x){ return x + 1; };
std::cout << "foo(5) = " << func(5) << std::endl;
func = [](int x){ return x * 2; };
std::cout << "bar(5) = " << func(5) << std::endl;
}
注意我们没有乱用函数指针声明或库类型吗? read/write 对每个人都好得多。 One thing to notice in this example is the unary +
operator; to perform a conversion to function pointer before assigning it to the variable. 它实际上看起来非常 实用 ,这似乎是您在这里想要实现的目标。
尝试通过 operator()
:
#include <iostream>
#include <functional>
int foo(int bar)
{
std::cout << bar << std::endl;
return 0;
}
template<typename /*Fn*/>
struct function_maker;
template<typename RTy, typename... ATy>
struct function_maker<RTy(ATy...)>
{
template<typename T>
static std::function<RTy(ATy...)> make_function(T&& fn)
{
return std::function<RTy(ATy...)>(std::forward<T>(fn));
}
};
template<typename /*Fn*/>
struct unwrap;
template<typename CTy, typename RTy, typename... ATy>
struct unwrap<RTy(CTy::*)(ATy...) const>
: function_maker<RTy(ATy...)> { };
template<typename CTy, typename RTy, typename... ATy>
struct unwrap<RTy(CTy::*)(ATy...)>
: function_maker<RTy(ATy...)> { };
template<typename T>
auto func(T f)
-> decltype(unwrap<decltype(&T::operator())>::make_function(std::declval<T>()))
{
return unwrap<decltype(&T::operator())>::make_function(std::forward<T>(f));
}
int main()
{
//auto barp = func(foo); // compilation error
auto bar = func(std::function<void(int)>(foo));
auto bar2 = func([](int)
{
// ...
});
bar(0); // prints 0
}