在我第一次尝试使用可变参数模板时,我正在尝试创建一个函数,该函数使用模板参数的所有组合来实例化派生类。拥有运行时选项类的想法是能够选择要使用的确切模板实例化。我已经能够使用基础案例来处理1和2模板参数。但是一般情况不起作用,下面的SelectInstance的第三次调用无法编译,出现以下错误:
候选函数不可行:需要3个参数,但提供了2个参数 AbstractBase * SelectInstance(Func func,Args ... args)
完整的代码如下:
#include <memory>
#include <iostream>
struct Options
{
bool GetFirstParameter() { return true; }
bool GetSecondParameter() { return false; }
bool GetThirdParameter() { return true; }
};
struct AbstractBase
{
virtual void PrintMe() = 0;
};
template<class... Args>
struct Derived : public AbstractBase
{
virtual void PrintMe() { std::cout << __PRETTY_FUNCTION__ << std::endl; }
};
template<class... Args>
AbstractBase *SelectInstance()
{
return new Derived<Args...>();
}
template<class Func, class... UnpackedArgs>
AbstractBase *SelectInstance(Func func)
{
if (func())
return SelectInstance<float, UnpackedArgs...>();
else
return SelectInstance<double, UnpackedArgs...>();
}
template<class Func, class... Args, class... UnpackedArgs>
AbstractBase *SelectInstance(Func func, Args... args)
{
if (func())
return SelectInstance<Args..., float, UnpackedArgs...>(args...);
else
return SelectInstance<Args..., double, UnpackedArgs...>(args...);
}
int main()
{
Options opts;
std::unique_ptr<AbstractBase> one(
SelectInstance(
std::bind(&Options::GetFirstParameter, &opts)
)
);
one->PrintMe();
std::unique_ptr<AbstractBase> two(
SelectInstance(
std::bind(&Options::GetFirstParameter, &opts),
std::bind(&Options::GetSecondParameter, &opts)
)
);
two->PrintMe();
// this one fails to compile!
std::unique_ptr<AbstractBase> three(
SelectInstance(
std::bind(&Options::GetFirstParameter, &opts),
std::bind(&Options::GetSecondParameter, &opts),
std::bind(&Options::GetThirdParameter, &opts)
)
);
three->PrintMe();
}
我可能会错误地使用可变参数模板将仿函数转换为新的参数包。任何指导表示赞赏。
答案 0 :(得分:1)
您的尝试失败的原因是编译器无法分辨您提供的参数。假设我们的函数类型为A
,B
和C
,为简单起见。最初的电话是:
// Func = A, Args... = {B, C}, UnpackedArgs... = {}
AbstractBase *SelectInstance(Func func, Args... args)
从这里,我们致电SelectInstance<Args..., float, UnpackedArgs...>(args...);
也就是说,SelectInstance<B, C, float>(b, c);
您的意图是:
// Func = B, Args... = {C}, UnpackedArgs... = {float}
AbstractBase *SelectInstance(Func func, Args... args)
但真正的参数包是贪婪的,并采取一切。 Args...
获取您明确提供的每个后续类型参数。所以这个电话真的被解释为:
// Func = B, Args... = {C, float}, UnpackedArgs... = {}
AbstractBase *SelectInstance(Func func, Args... args)
这个函数有三个参数(一个B
,一个C
和一个float
),但你只传递两个(b
和c
),因此错误。
要解决此问题,请翻转模板参数的顺序,先将非推导出的参数放入,然后推断出推断的参数。这样,您仍然可以使用模板推导来做正确的事情,甚至避免额外的过载:
template<class... Args>
AbstractBase *SelectInstance()
{
return new Derived<Args...>();
}
template<class... UnpackedArgs, class Func, class... Args>
AbstractBase *SelectInstance(Func func, Args... args)
{
if (func()) {
return SelectInstance<float, UnpackedArgs...>(args...);
}
else {
return SelectInstance<double, UnpackedArgs...>(args...);
}
}
这是有效的,因为现在您提供的所有参数都会进入UnpackedArgs...
,而演绎会计算出Func
和Args...
- 这正是您想要的。