考虑下面的函数模板:
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与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编译罚款,但给出了使用铛与苹果LLVM版本10.0.0(铛-1000.11.45.5)的错误。
铛误差
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) {}
为什么会出现以下情况[?]
在计取,当你打电话
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...
可变参数列表,但只有第一种类型。
如果我没有错,问题是,编译器尝试从ArgTypes...
参数推断bar
的其余部分,但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
,并且只要签名完全匹配它的工作原理。