实例化函数模板的编译问题

时间:2015-04-29 10:48:48

标签: c++ c++11 language-lawyer variadic-templates function-templates

请考虑以下代码:

#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>已失败。有人可以给我一些线索,为什么会这样?我认为这与参数包有关。

2 个答案:

答案 0 :(得分:5)

您的代码格式错误。根据[temp.deduct.type]:

mem_fn位于非推导的上下文中
  

未推断的背景是:
     - ......
     - 不在 parameter-declaration-list 末尾出现的函数参数包。

来自[temp.param]:

  

不应遵循功能模板的模板参数包   另一个模板参数,除非可以从 parameter-type-list 推断该模板参数   函数模板或具有默认参数(14.8.2)。 [例如:

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>;

答案 1 :(得分:2)

Barry解决方案的一个更通用的替代方法可以使用decltype来推断整个成员函数签名,包括它所属的类:

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;