c++ - 使用多个参数包将运行时值转换为编译模板参数

标签 c++ templates variadic-templates

在我第一次涉足可变参数模板时,我试图创建一个函数来实例化具有模板参数的所有组合的派生类。想法是让运行时选项类能够选择要使用的确切模板实例。我已经能够让基本案例使用 1 和 2 模板参数。但是一般情况下是行不通的,下面第三次调用 SelectInstance 编译失败,出现以下错误:

candidate function not viable: requires 3 arguments, but 2 were provided 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();
}

我可能错误地使用可变参数模板将仿函数转换为新的参数包。任何指导表示赞赏。

最佳答案

您尝试失败的原因是编译器无法判断您为什么提供了哪些参数。假设我们的函数类型是 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)

此函数采用三个参数(BCfloat),但您只传递两个参数(bc),因此出现错误。


要解决此问题,请翻转模板参数的顺序,将推导的参数放在第一位,将推导的参数放在最后。这样,您仍然可以使用模板推导来做正确的事情,甚至可以避免额外的重载:

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...和扣除数字 FuncArgs... - 这正是你想要的。

关于c++ - 使用多个参数包将运行时值转换为编译模板参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37121693/

相关文章:

c++ - 在带有 Qt 的 Linux Ubuntu 平台上使用 OpenCV 2.2 显示图像

具有灵活返回类型的 C++ 函数模板

c++ - 如何使 friend 类的层次结构?

c++ - 获取参数包的前 N ​​个元素

c++ - 使用基于模板参数的一组函数重载构建可变参数模板类?

C++ - 如何从可变数量的基数中引入重载集。

c++ - XCode 4.6.2 中的 Stanford Engineering Everywhere CS106B C++ 库

c++ - 虚函数中的类转换 C++

c++ - 从txt文件读取并创建对象

c++ - 如何设计嵌套类是同一类型的嵌套模板类