根据我的理解,下面的程序显然应该打印出来:
1.0 hello world 42
然而,它无法编译。为什么呢?
#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"));
}
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"));
^
答案 0 :(得分:5)
首先,"hello world"
不会推断为std::string
,它会推断为const char*
,与Callee
不匹配,所以让我们确定您的通话是否合格而是"hello world"s
。
其次,有一个类型的参数似乎存在一些问题:
void (*funcPtr)(InitialArgTypes..., int)
显然,在非推断的上下文和可推导的上下文之间存在某种不确定性 - 因为它不是非推导的上下文(否则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);
}
现在,InitialArgTypes...
将从最后传递的参数中推断出来。这就是我们想要的,所以这有效:
CallWithExtraParameter(Callee, 1.0, "hello world"s);
答案 1 :(得分:1)
为什么已在另一个答案中解释 无论如何,我想发布一个建议再提出一个解决方案 它遵循一个工作示例:
#include <iostream>
#include <string>
using namespace std;
template<class... C>
struct Fn {
using type = void (*)(C..., int);
};
template<class... InitialArgTypes>
void CallWithExtraParameter(typename Fn<InitialArgTypes...>::type funcPtr, 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"));
}
答案 2 :(得分:0)
这是对你可能有用的任何大小尾巴的概括。关于可调用类型,它也更通用(例如,此处也测试了成员函数指针)。
#include <iostream>
#include <tuple>
#include <utility>
#include <string>
template <typename Callable> struct Invoke;
template <typename R, typename... Args>
struct Invoke<R(*)(Args...)> {
template <typename F, typename Tuple, std::size_t... Is, typename... As>
static R execute (F funcPtr, Tuple&& tuple, std::index_sequence<Is...>, As&&... as) {
return (*funcPtr)(std::forward<As>(as)..., std::get<Is>(std::forward<Tuple>(tuple))...);
}
};
template <typename R, typename C, typename... Args>
struct Invoke<R(C::*)(Args...)> {
template <typename F, typename Tuple, std::size_t... Is, typename... As>
static R execute (F f, Tuple&& tuple, std::index_sequence<Is...>, C& c, As&&... as) {
return (c.*f)(std::forward<As>(as)..., std::get<Is>(std::forward<Tuple>(tuple))...);
}
template <typename F, typename Tuple, std::size_t... Is, typename... As>
static R execute (F f, Tuple&& tuple, std::index_sequence<Is...>, C* c, As&&... as) {
return (c->*f)(std::forward<As>(as)..., std::get<Is>(std::forward<Tuple>(tuple))...);
}
};
template <typename R, typename C, typename... Args>
struct Invoke<R(C::*)(Args...) const> {
template <typename F, typename Tuple, std::size_t... Is, typename... As>
static R execute (F f, Tuple&& tuple, std::index_sequence<Is...>, C& c, As&&... as) {
return (c.*f)(std::forward<As>(as)..., std::get<Is>(std::forward<Tuple>(tuple))...);
}
template <typename F, typename Tuple, std::size_t... Is, typename... As>
static R execute (F f, Tuple&& tuple, std::index_sequence<Is...>, const C* c, As&&... as) {
return (c->*f)(std::forward<As>(as)..., std::get<Is>(std::forward<Tuple>(tuple))...);
}
};
template <typename Functor>
struct Invoke : Invoke<decltype(&Functor::operator())> {};
// etc...
template <typename R = void, typename F, typename Tuple, typename... Args>
R invokeWithTupleTail (F funcPtr, Tuple&& tuple, Args&&... args) {
return Invoke<F>::execute(funcPtr, std::forward<Tuple>(tuple),
std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>{}, std::forward<Args>(args)...);
}
// Testing
struct Thing {
int call (char k, int n, double a, std::string b, int c) {
std::cout << k << ' ' << n << ' ' << a << ' ' << b << ' ' << c << '\n';
return 5;
}
int doIt (char k, int n, double a, std::string b, int c) const {
std::cout << k << ' ' << n << ' ' << a << ' ' << b << ' ' << c << '\n';
return 12;
}
int operator() (char k, int n, double a, std::string b, int c) const {
std::cout << k << ' ' << n << ' ' << a << ' ' << b << ' ' << c << '\n';
return 20;
}
};
void foo (char k, int n, double a, std::string b, int c) {
std::cout << k << ' ' << n << ' ' << a << ' ' << b << ' ' << c << '\n';
}
int bar (char k, int n, double a, std::string b, int c) {
std::cout << k << ' ' << n << ' ' << a << ' ' << b << ' ' << c << '\n';
return 10;
}
int main() {
const auto tupleTail = std::make_tuple(1.5, std::string("hello"), 42);
invokeWithTupleTail(foo, tupleTail, 'a', 8); // a 8 1.5 hello world 42
int a = invokeWithTupleTail<int>(&bar, tupleTail, 'a', 8); // a 8 1.5 hello world 42
std::cout << a << '\n'; // 10
Thing thing;
a = invokeWithTupleTail<int>(&Thing::call, tupleTail, thing, 'a', 8); // a 8 1.5 hello world 42
std::cout << a << '\n'; // 5
a = invokeWithTupleTail<int>(&Thing::doIt, tupleTail, &thing, 'a', 8); // a 8 1.5 hello world 42
std::cout << a << '\n'; // 12
a = invokeWithTupleTail<int>(&Thing::operator(), tupleTail, thing, 'a', 8); // a 8 1.5 hello world 42
std::cout << a << '\n'; // 20
}