将函数传递给可变函数模板
Passing a function to a variadic function template
考虑以下函数模板:
template<typename RetType, typename... ArgTypes>
void foo0(std::function<RetType(ArgTypes...)> f) {}
template<typename RetType, typename ArgType>
void foo1(std::function<RetType(ArgType)> f) {}
以及以下函数:
void bar(int n) {}
为什么会出现以下情况:
foo0<void, int>(bar); // does not compile
foo1<void, int>(bar); // compiles fine
编译错误为(gcc-8 with C++17):
error: no matching function for call to 'foo0<void, int>(void (&)(int))'
foo0<void, int>(bar);
^
note: candidate: 'template<class RetType, class ... ArgTypes> void foo0(std::function<_Res(_ArgTypes ...)>)'
void foo0(std::function<RetType(ArgTypes...)> f) {}
^~~~
note: template argument deduction/substitution failed:
note: mismatched types 'std::function<void(_ArgTypes ...)>' and 'void (*)(int)'
foo0<void, int>(bar);
使用虚拟模板
template<typename T>
void bar(int n) {}
使 foo0<void, int>(bar<int>);
在 gcc-8 中编译正常,但在 Apple LLVM 版本 10.0.0 (clang-1000.11.45.5) 中使用 clang 时出错。
clang 错误是
error: no matching function for call to 'foo0'
foo0<void, int>(bar<int>);
^~~~~~~~~~~~~~~
note: candidate template ignored: could not match 'function<void (int, type-parameter-0-1...)>' against 'void (*)(int)'
void foo0(std::function<RetType(ArgTypes...)> f) {}
Why does the following occur [?]
打电话的时候算一下
foo0<void, int>(bar); // compilation error
foo1<void, int>(bar); // compile
foo0()
和 foo1()
期望 std::function
并且 bar
是指向可以转换为 std::function
但不是的函数的指针std::function
.
在 foo1()
的情况下,您显式显示了 RetType
和 ArgType
模板参数,因此编译器可以将 bar
转换为 std::function<void(int)>
和所有顺利。
但是 foo0()
的情况不同,因为模板参数 ArgTypes...
是一个可变参数并且调用 foo0<void, int>(bar)
你没有显式完整的 ArgTypes...
可变参数列表但是只有第一种。
如果我没记错的话,问题是编译器试图从 bar
参数中推导出 ArgTypes...
的其余部分,但 bar
不是 std::function
所以编译器无法推导出 ArgTypes...
的其余部分,所以错误。
我想
foo0<void, int>(std::function<void(int)>{bar});
或干脆
foo0(std::function<void(int)>{bar});
或(仅限 C++17)也
foo0(std::function{bar});
应该编译,因为以这种方式调用 foo0()
,函数接收一个 std::function
,因此编译器可以完全推导出模板参数。
我不明白带有虚拟模板参数bar()
的版本如何
foo0<void, int>(bar<int>);
可以用 g++-8 编译,我想这是一个 g++ 错误。
template<typename RetType, typename... ArgTypes>
void foo0(std::function<RetType(ArgTypes...)> f) {}
修复方法是:
template<class X>struct tag_t{using type=X;};
template<class X>using block_deduction = typename tag_t<X>::type;
template<typename RetType, typename... ArgTypes>
void foo0(block_deduction_t<std::function<RetType(ArgTypes...)>> f) {}
现在你的 foo0<void, int>(bar)
可以编译了。
一般的问题是说 foo0<void, int>
,你不是在说“RetType
是 void
而 ArgTypes...
是 int
。你是说ArgTypes...
开头为 int
。
std::function<void(int, double)> x;
foo0<void, int>( x )
以上编译正常。
...
c++17 中的另一种方法是添加另一个重载。
留下这个:
template<typename RetType, typename... ArgTypes>
void foo2(block_deduction_t<std::function<RetType(ArgTypes...)>> f) {}
但添加:
template<typename RetType, typename... ArgTypes, class F>
void foo2(F&& f) {
return foo2<RetType, ArgTypes...>( std::function{std::forward<F>(f)} );
}
template<int unused, class F>
void foo2(F&& f) {
return foo2( std::function{std::forward<F>(f)} );
}
这里我们把F
包装成一个构造引导的std::function
。
您对 foo2<int, void>( bar )
的调用现在调用 foo2<void, int, decltype(bar)&>
,这是第二次重载。然后它继续从中构造一个 std::function
,只要签名完全匹配它就可以工作。
考虑以下函数模板:
template<typename RetType, typename... ArgTypes>
void foo0(std::function<RetType(ArgTypes...)> f) {}
template<typename RetType, typename ArgType>
void foo1(std::function<RetType(ArgType)> f) {}
以及以下函数:
void bar(int n) {}
为什么会出现以下情况:
foo0<void, int>(bar); // does not compile
foo1<void, int>(bar); // compiles fine
编译错误为(gcc-8 with C++17):
error: no matching function for call to 'foo0<void, int>(void (&)(int))'
foo0<void, int>(bar);
^
note: candidate: 'template<class RetType, class ... ArgTypes> void foo0(std::function<_Res(_ArgTypes ...)>)'
void foo0(std::function<RetType(ArgTypes...)> f) {}
^~~~
note: template argument deduction/substitution failed:
note: mismatched types 'std::function<void(_ArgTypes ...)>' and 'void (*)(int)'
foo0<void, int>(bar);
使用虚拟模板
template<typename T>
void bar(int n) {}
使 foo0<void, int>(bar<int>);
在 gcc-8 中编译正常,但在 Apple LLVM 版本 10.0.0 (clang-1000.11.45.5) 中使用 clang 时出错。
clang 错误是
error: no matching function for call to 'foo0'
foo0<void, int>(bar<int>);
^~~~~~~~~~~~~~~
note: candidate template ignored: could not match 'function<void (int, type-parameter-0-1...)>' against 'void (*)(int)'
void foo0(std::function<RetType(ArgTypes...)> f) {}
Why does the following occur [?]
打电话的时候算一下
foo0<void, int>(bar); // compilation error
foo1<void, int>(bar); // compile
foo0()
和 foo1()
期望 std::function
并且 bar
是指向可以转换为 std::function
但不是的函数的指针std::function
.
在 foo1()
的情况下,您显式显示了 RetType
和 ArgType
模板参数,因此编译器可以将 bar
转换为 std::function<void(int)>
和所有顺利。
但是 foo0()
的情况不同,因为模板参数 ArgTypes...
是一个可变参数并且调用 foo0<void, int>(bar)
你没有显式完整的 ArgTypes...
可变参数列表但是只有第一种。
如果我没记错的话,问题是编译器试图从 bar
参数中推导出 ArgTypes...
的其余部分,但 bar
不是 std::function
所以编译器无法推导出 ArgTypes...
的其余部分,所以错误。
我想
foo0<void, int>(std::function<void(int)>{bar});
或干脆
foo0(std::function<void(int)>{bar});
或(仅限 C++17)也
foo0(std::function{bar});
应该编译,因为以这种方式调用 foo0()
,函数接收一个 std::function
,因此编译器可以完全推导出模板参数。
我不明白带有虚拟模板参数bar()
的版本如何
foo0<void, int>(bar<int>);
可以用 g++-8 编译,我想这是一个 g++ 错误。
template<typename RetType, typename... ArgTypes>
void foo0(std::function<RetType(ArgTypes...)> f) {}
修复方法是:
template<class X>struct tag_t{using type=X;};
template<class X>using block_deduction = typename tag_t<X>::type;
template<typename RetType, typename... ArgTypes>
void foo0(block_deduction_t<std::function<RetType(ArgTypes...)>> f) {}
现在你的 foo0<void, int>(bar)
可以编译了。
一般的问题是说 foo0<void, int>
,你不是在说“RetType
是 void
而 ArgTypes...
是 int
。你是说ArgTypes...
开头为 int
。
std::function<void(int, double)> x;
foo0<void, int>( x )
以上编译正常。
...
c++17 中的另一种方法是添加另一个重载。
留下这个:
template<typename RetType, typename... ArgTypes>
void foo2(block_deduction_t<std::function<RetType(ArgTypes...)>> f) {}
但添加:
template<typename RetType, typename... ArgTypes, class F>
void foo2(F&& f) {
return foo2<RetType, ArgTypes...>( std::function{std::forward<F>(f)} );
}
template<int unused, class F>
void foo2(F&& f) {
return foo2( std::function{std::forward<F>(f)} );
}
这里我们把F
包装成一个构造引导的std::function
。
您对 foo2<int, void>( bar )
的调用现在调用 foo2<void, int, decltype(bar)&>
,这是第二次重载。然后它继续从中构造一个 std::function
,只要签名完全匹配它就可以工作。