我有一个模板函数,它接受一个指针,该指针指向任何类型的成员函数,具有任意数量的参数(但强制执行一些规则-它必须是空的,最后一个参数必须是指针):
class Foo {
public:
Foo() {}
template<typename T, typename Out, typename ... In>
void foo(T *obj, void(T::*func)(In ..., Out*)) {
...
}
...
};
当我试图调用该函数时,我得到一个类型不匹配错误:
class Bar {
public:
Bar() {}
void bar(int in, bool *out) {
...
}
};
int main()
{
Foo foo;
Bar bar;
foo.foo<Bar, bool, int>(&bar, &Bar::bar);
...
}
错误:test.cpp: In function 'int main()':
test.cpp:41:44: error: no matching function to call to 'Foo::foo(Bar*, void (Bar::*)(int, bool*))'
foo.foo<Bar, bool, int>(&bar, &Bar::bar);
^
test.cpp:24:10: note: candidate: template<class T, class Out, class ... In> void Foo::foo(T*, void (T::*)(In ..., Out*))
void foo(T *obj, void(T::*func)(In ..., Out*))
^
test.cpp:24:10: note template argument deduction/substitution failed:
test.cpp:41:44: note mismatched types 'bool*' and 'int'
foo.foo<Bar, bool, int>(&bar, &Bar::bar);
^
有趣的是,当我使In
为简单类型而不是参数包时,它会编译并正确工作。在我看来,如果编译器没有在某个地方扩展包,并尝试将第二个参数(bool*
)匹配到第一个参数(int
),而不是第二个。
不确定为什么您的示例不起作用,但可能的解决方案是编写一个trait来检查参数包中的最后一个类型是否为指针,然后std::enable_if
:
template <typename... Ts>
struct last_is_pointer : std::false_type{};
template <typename T1, typename T2, typename... Ts>
struct last_is_pointer<T1, T2, Ts...> : last_is_pointer<T2,Ts...>{};
template <typename T>
struct last_is_pointer<T> : std::is_pointer<T>{};
class Foo {
public:
Foo() {}
template<typename T, typename... Args>
typename std::enable_if<last_is_pointer<Args...>::value>::type
foo(T *obj, void(T::*func)(Args...)) {
}
};
然后不指定任何模板参数,直接调用:
foo.foo(&bar, &Bar::bar);
现场演示
在对编译器进行了更多的研究之后,我发现了一个模板包装器形式的简单解决方案。显然,gcc和clang在Out*
之前扩展In ...
包在类型别名(using
或typedef
)中没有问题,只有当它是函数定义的一部分时才会遇到问题。
namespace detail {
template<typename T, typename Out, typename ... In>
struct funcHelper {
using type = void(T::*)(In ..., Out*);
};
}
class Foo {
public:
Foo() {}
template<typename T, typename Out, typename ... In>
void foo(T *obj, typename detail::funcHelper<T, Out, In ...>::type func) {
...
}
...
};
class Bar {
public:
Bar() {}
void bar(int in, bool *out) {
...
}
};
int main()
{
Foo foo;
Bar bar;
foo.foo<Bar, bool, int>(&bar, &Bar::bar);
...
}