这个程序用参数包调用函数指针有什么问题?

use*_*751 11 c++ templates variadic-templates c++11

根据我的理解,以下程序显然应该打印:

1.0 hello world 42
Run Code Online (Sandbox Code Playgroud)

但是,它无法编译.为什么?

#include <iostream>
#include <string>
using namespace std;

template<class... InitialArgTypes>
void CallWithExtraParameter(void (*funcPtr)(InitialArgTypes..., int), InitialArgTypes... initialArgs)
{
    (*funcPtr)(initialArgs..., 42);
}

void Callee(double a, string b, int c)
{
    cout << a << " " << b << " " << c << endl;
}

int main()
{
    CallWithExtraParameter<double, string>(Callee, 1.0, string("hello world"));
}
Run Code Online (Sandbox Code Playgroud)

编译器输出:

prog.cpp: In function 'int main()':
prog.cpp:18:75: error: no matching function for call to 'CallWithExtraParameter(void (&)(double, std::string, int), double, std::string)'
  CallWithExtraParameter<double, string>(Callee, 1.0, string("hello world"));
                                                                           ^
prog.cpp:6:6: note: candidate: template<class ... InitialArgTypes> void CallWithExtraParameter(void (*)(InitialArgTypes ..., int), InitialArgTypes ...)
 void CallWithExtraParameter(void (*funcPtr)(InitialArgTypes..., int), InitialArgTypes... initialArgs)
      ^
prog.cpp:6:6: note:   template argument deduction/substitution failed:
prog.cpp:18:75: note:   mismatched types 'int' and 'double'
  CallWithExtraParameter<double, string>(Callee, 1.0, string("hello world"));
                                                                           ^
Run Code Online (Sandbox Code Playgroud)

Bar*_*rry 5

首先,"hello world"不会推断std::string,它会推断const char*,哪个不匹配Callee,所以让我们修复你的通话"hello world"s而不是通过.

其次,有一个类型的参数似乎存在一些问题:

void (*funcPtr)(InitialArgTypes..., int)
Run Code Online (Sandbox Code Playgroud)

这显然是在非演绎语境和可推导语境之间的某种不确定性 - 因为它不是非演绎语境(否则InitialArgTypes...将从其他参数中推断出来)并且它不可推导(因为它仍然失败).因此,让我们更进一步,明确地将其作为一个非演绎的背景:

template <class T> struct identity { using type = T; };
template <class T> using identity_t = typename identity<T>::type;

template <class... InitialArgTypes>
void CallWithExtraParameter(void (*funcPtr)(identity_t<InitialArgTypes>..., int),
        InitialArgTypes... initialArgs)
{
    (*funcPtr)(initialArgs..., 42);
}
Run Code Online (Sandbox Code Playgroud)

现在,InitialArgTypes...将从最后传递的论点中推断出来.这是我们想要的,所以这是有效的:

CallWithExtraParameter(Callee, 1.0, "hello world"s);
Run Code Online (Sandbox Code Playgroud)

  • 字符串是否被推断为`const char [N]`? (4认同)
  • 我没有明确地将模板参数指定为`<double,string>`吗? (2认同)