我正在尝试将lambda传递给采用模板函数类型的模板函数。在编译时出现candidate template ignored: could not match...
错误。
但是,当我尝试将相同的lambda传递给采用模板函数类型的模板类时,它将编译并起作用。
请考虑以下代码(c ++ 17)
#include <functional>
// define the template function type
template<typename T, typename...S>
using compose_fn_t = std::function<T(S...)>;
// define a template function which accepts the template function type
template<typename T, typename... S>
void compose(compose_fn_t<T, S...> fn) {};
// define a template class which accepts the template function type
template<typename T, typename... S>
class Compose {
public:
Compose(compose_fn_t<T, S...> fn) {};
};
// some arbitrary types
struct A{};
struct B{};
struct C{};
int main() {
compose<A, B, C>
([] (B b, C c) -> A { return {}; }); // this will not compile!
Compose<A, B, C>
([] (B b, C c) -> A { return {}; }); // this compiles and runs correctly!
return 0;
}
当我使用compose<A, B, C>
进行编译时,它将引发以下错误
$ g++ -std=c++17 -o main main.cpp
main.cpp:18:3: error: no matching function for call to 'compose'
compose<A, B, C>
^~~~~~~~~~~~~~~~
main.cpp:8:6: note: candidate template ignored: could not match 'function<A (B, C, type-parameter-0-1...)>' against '(lambda at main.cpp:19:6)'
void compose(compose_fn_t<T, S...> fn) {
^
1 error generated.
模板功能类型(type-parameter-0-1
)期望这种附加的compose_fn_t
类型是什么?
在类情况下(Compose<A, B, C>
),当您指定模板参数时/之后不会进行任何扣除。我们有T = A
和...S = B, C
。一切正常。
但是在函数情况下(compose<A, B, C>(...)
),编译器不知道这些是否都是模板参数,还是不应该推导其他参数。您可以从错误中收集信息:它提到function<A (B, C, type-parameter-0-1...)>
,即在C
之后不会停止。
当您尝试传递带有三个参数的std::function
时,这种区别变得很明显:
std::function<A(B, C, D)> f;
compose<A, B, C>(f);
[此处,编译器推导compose<A, B, C, D>
,即T = A
和...S = B, C, D
。之所以进行编译,是因为编译器会尝试推导compose
的S
包的其他参数。
Compose<A, B, C>(f);
无法编译:f
与compose_fn_t<A, B, C>
不匹配。编译器不执行Compose
模板参数的推导。
如果您为函数调用指定模板参数列表,如compose<A, B, C>
,则此列表如果模板参数比参数多,则被认为是部分列表。
调用该函数仍然会对其余模板参数进行模板参数推导。
在您的情况下,将根据compose_fn_t
参数推导出参数包的其余参数(已确定前三个模板参数,但已确定),但是失败,因为无法将lambda推导为std::function
类型。 >
为了避免这种情况,您需要将函数参数中使用的模板参数强制进入非推论上下文。一种方法是使用
template<typename T, typename... S> void compose(typename std::type_identity<compose_fn_t<T, S...>>::type fn) {};
因为未推导范围解析运算符
::
左侧的所有内容。但是,这也意味着没有模板参数列表,您将无法调用该函数。
std::type_identity
是C ++ 20的功能,但是您可以轻松实现自己的功能。它什么也不做,只返回在其value
成员中给定的类型:
template<typename T> struct type_identity { using type = T; };
这不是类模板的问题,因为仅在根本不提供模板参数列表的情况下,才执行类模板参数推导。
您的问题与c ++中的隐式转换有关。