【问题标题】:Working with multiple parameter packs to convert runtime values into compile template arguments使用多个参数包将运行时值转换为编译模板参数
【发布时间】:2016-05-09 17:14:32
【问题描述】:

在我第一次尝试可变参数模板时,我试图创建一个函数,该函数使用模板参数的所有组合实例化派生类。这个想法是让运行时选项类能够选择要使用的确切模板实例化。我已经能够让基本案例使用 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();
}

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

【问题讨论】:

    标签: c++ templates variadic-templates


    【解决方案1】:

    您的尝试失败的原因是编译器无法判断您提供了哪些参数。为简单起见,假设我们的函数类型为 ABC。初始调用是:

    // Func = A, Args... = {B, C}, UnpackedArgs... = {}
    AbstractBase *SelectInstance(Func func, Args... args)
    

    从这里开始,我们调用SelectInstance&lt;Args..., float, UnpackedArgs...&gt;(args...);,也就是说,SelectInstance&lt;B, C, float&gt;(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... - 这正是您想要的。

    【讨论】:

    • 谢谢。一旦我写了额外的超载,我觉得我走错了路。请注意,我将其翻转为“SelectInstance”以保持解包顺序与作为参数传入的仿函数相同。
    猜你喜欢
    • 2012-06-16
    • 1970-01-01
    • 2012-12-11
    • 1970-01-01
    • 2015-12-21
    • 2015-05-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多