实例化函数模板的编译问题
Compilation issue with instantiating function template
考虑以下代码:
#include <iostream>
struct S {
void f(const char* s) {
std::cout << s << '\n';
}
};
template <typename... Args, void(S::*mem_fn)(Args...)>
void invoke(S* pd, Args... args) {
(pd->*mem_fn)(args...);
}
int main() {
S s;
void(*pfn)(S*, const char*) = invoke<const char*, &S::f>;
pfn(&s, "hello");
}
编译代码时,clang报错如下:
main.cpp:16:33: error: address of overloaded function 'invoke' does not match required type 'void (S *, const char *)'
void(*pfn)(S*, const char*) = invoke<const char*, &S::f>
^~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:10:6: note: candidate template ignored: invalid explicitly-specified argument for template parameter 'Args'
void invoke(S* pd, Args... args) {
^
1 error generated.
该消息似乎表明模板实例化 invoke<const char*, &S::f>
失败。有人可以给我一些关于为什么会这样的线索吗?我相信这与参数包有关。
您的代码格式错误。 mem_fn
根据 [temp.deduct.type] 处于非推导上下文中:
The non-deduced contexts are:
— ...
— A function parameter pack that does not occur at the end of the parameter-declaration-list.
来自[temp.param]:
A template parameter pack of a function template shall not be followed
by another template parameter unless that template parameter can be deduced from the parameter-type-list
of the function template or has a default argument (14.8.2). [ Example:
template<class T1 = int, class T2> class B; // error
// U can be neither deduced from the parameter-type-list nor specified
template<class... T, class... U> void f() { } // error
template<class... T, class U> void g() { } // error
—end example ]
此声明中的 mem_fn
参数:
template <typename... Args, void(S::*mem_fn)(Args...)>
void invoke(S* pd, Args... args) {
遵循模板参数包。它不能从列表中推断出来。但是,您可以将其作为参数传递:
template <typename... Args>
void invoke(S* pd, void(S::*mem_fn)(Args...), Args... args);
或者将整个东西包装在一个结构中,这样您就不需要在参数包后面加上另一个模板:
template <typename... Args>
struct Invoke {
template <void (S::*mem_fn)(Args...)>
static void invoke(S* pd, Args... args);
};
void(*pfn)(S*, const char*) = Invoke<const char*>::invoke<&S::f>;
比 Barry 的解决方案稍微更通用的替代方案可以使用 decltype
来推导整个成员函数签名,包括它所属的 class:
template <typename Sig, Sig Fn>
struct invoke;
template <typename Ret, class Class, typename... Args, Ret(Class::*mem_fn)(Args...)>
struct invoke <Ret(Class::*)(Args...), mem_fn>
{
static void exec (Class* pd, Args... args)
{
(pd->*mem_fn)(args...);
}
};
然后像这样使用它:
void(*pfn)(S*, const char*) = invoke<decltype(&S::f),&S::f>::exec;
考虑以下代码:
#include <iostream>
struct S {
void f(const char* s) {
std::cout << s << '\n';
}
};
template <typename... Args, void(S::*mem_fn)(Args...)>
void invoke(S* pd, Args... args) {
(pd->*mem_fn)(args...);
}
int main() {
S s;
void(*pfn)(S*, const char*) = invoke<const char*, &S::f>;
pfn(&s, "hello");
}
编译代码时,clang报错如下:
main.cpp:16:33: error: address of overloaded function 'invoke' does not match required type 'void (S *, const char *)'
void(*pfn)(S*, const char*) = invoke<const char*, &S::f>
^~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:10:6: note: candidate template ignored: invalid explicitly-specified argument for template parameter 'Args'
void invoke(S* pd, Args... args) {
^
1 error generated.
该消息似乎表明模板实例化 invoke<const char*, &S::f>
失败。有人可以给我一些关于为什么会这样的线索吗?我相信这与参数包有关。
您的代码格式错误。 mem_fn
根据 [temp.deduct.type] 处于非推导上下文中:
The non-deduced contexts are:
— ...
— A function parameter pack that does not occur at the end of the parameter-declaration-list.
来自[temp.param]:
A template parameter pack of a function template shall not be followed by another template parameter unless that template parameter can be deduced from the parameter-type-list of the function template or has a default argument (14.8.2). [ Example:
template<class T1 = int, class T2> class B; // error // U can be neither deduced from the parameter-type-list nor specified template<class... T, class... U> void f() { } // error template<class... T, class U> void g() { } // error
—end example ]
此声明中的 mem_fn
参数:
template <typename... Args, void(S::*mem_fn)(Args...)>
void invoke(S* pd, Args... args) {
遵循模板参数包。它不能从列表中推断出来。但是,您可以将其作为参数传递:
template <typename... Args>
void invoke(S* pd, void(S::*mem_fn)(Args...), Args... args);
或者将整个东西包装在一个结构中,这样您就不需要在参数包后面加上另一个模板:
template <typename... Args>
struct Invoke {
template <void (S::*mem_fn)(Args...)>
static void invoke(S* pd, Args... args);
};
void(*pfn)(S*, const char*) = Invoke<const char*>::invoke<&S::f>;
比 Barry 的解决方案稍微更通用的替代方案可以使用 decltype
来推导整个成员函数签名,包括它所属的 class:
template <typename Sig, Sig Fn>
struct invoke;
template <typename Ret, class Class, typename... Args, Ret(Class::*mem_fn)(Args...)>
struct invoke <Ret(Class::*)(Args...), mem_fn>
{
static void exec (Class* pd, Args... args)
{
(pd->*mem_fn)(args...);
}
};
然后像这样使用它:
void(*pfn)(S*, const char*) = invoke<decltype(&S::f),&S::f>::exec;