【问题标题】:Multiple Variadic Parameter Pack for Template Class模板类的多变量参数包
【发布时间】:2016-09-09 02:00:02
【问题描述】:

我正在使用可变参数包进行基于策略的类设计。

template <APITypes APIType, class... Policies>
class IShader : public Policies... {

};

策略在调用时定义,如果未指定,则使用默认值。当我需要添加另一个可变参数包时,问题就来了:

template <AttributeType... Attributes, APITypes APIType, class... Policies>
class IShader : public Policies... {

};

这会导致错误“模板参数包必须是最后一个模板参数”。我计划使用属性包来更改至少一项策略的行为。但我不知道如何在一个模板类中获取两个可变参数包。

【问题讨论】:

  • 你不能。您需要重新考虑您的模板/类设计。
  • 甚至没有某种间接?为属性列表说某种包装器。还是模板的模板?
  • 你总是可以考虑像IShader&lt;Attributes&lt;A1, A2&gt;, APIType, Policies&lt;P1, P2&gt;&gt;
  • 有点像你无法定义void foo(int numAttributes, ..., int numPolicies, ...);
  • @chris 这是调用时的样子吗?我尝试使用围绕属性的包装器进行类似的操作,但无法弄清楚如何将所述包装器放入模板中或使列表可供需要它的策略访问。

标签: c++ c++14 variadic-templates


【解决方案1】:

我认为最简单的答案是为您的参数包创建模板类型包装器。例如:

template <AttributeType... T>
struct Attributes {};

template <typename... T>
struct Policies {};

然后你可以声明你的 IShader 类型:

template <typename... T>
class IShader;

将您的实现创建为专业化。请注意,在一个特化中,您可以有多个参数包参数。

template <AttributeType... AttributeList, ApiTypes APIType, typename... PolicyList>
class IShader<Attributes<AttributeList...>, ApiType, Policies<PolicyList...>> 
    : public PolicyList...
{
    ...
};

然后您甚至可以允许用户以不同的顺序指定参数(如果通过继承这样做,请确保您转发构造函数):

template <AttributeType... AttributeList, ApiTypes APIType, typename... PolicyList>
struct IShader<ApiType, Policies<PolicyList...>, Attributes<AttributeList...>
    : public IShader<Attributes<AttributeList...>, ApiType, Policies<PolicyList...>>
{
    using IShader<Attributes<AttributeList...>, ApiType, Policies<PolicyList...>>::IShader;
};

如果您真的很喜欢,您甚至可以使用元编程技巧来允许以任意顺序的参数,而无需枚举所有顺序。这留给读者作为练习。 :)

【讨论】:

    【解决方案2】:

    在讨论 cmets 中,您表示愿意考虑某种间接方式,或“属性列表的某种包装器”。

    基于std::tuple 的轻量级包装器以及专业化可能在这里工作:

    template <typename attribute_tuple, APITypes APIType,
              typename policy_tuple> class IShader;
    
    template <AttributeType... Attributes, APITypes APIType,
              class... Policies>
    class IShader<std::tuple<Attributes...>, APIType,
                  std::tuple<Policies...>> : public Policies... {
    
    // ...
    
    };
    

    这里的目标是按照以下方式使用模板实例:

    IShared<std::tuple<Attribute1, Attribute2>, APITypeFoo,
            std::tuple<Policy1, Policy2>> ishared_instance;
    

    请注意,这将与专用模板声明相匹配,此时两个参数包都可供模板专用化单独使用。

    【讨论】:

    • 在声明 IShader 类的对象时是否需要元组?
    • @James 需要元组,但只能作为一种类型。没有实际实例化元组。
    • 即使在这里IShared&lt;std::tuple&lt;Attribute1, Attribute2&gt;, APITypeFoo, std::tuple&lt;Policy1, Policy2&gt;&gt; ishared_instance;?那么它不会使用提供实现的专业化,而是使用默认值。你不能像IShared&lt;Attribute1, Attribute2, APITypeFoo, Policy1, Policy2&gt; ishared_instance;这样使用这个类吗?
    • @James 你的第一个成功,因为实现是最专业的匹配专业化。你的后一个例子会失败。模板包含三个参数。 attribute_tuple 获得第一个,APIType 获得第二个,policy_tuple 获得第三个。如果您不喜欢元组的外观,请创建自己的类型包装器,就像我在另一个答案中的解决方案中一样。
    • 所以特化不匹配模板参数,即template &lt;AttributeType... Attributes, APITypes APIType, class... Policies&gt;,而是匹配这部分class IShader&lt;std::tuple&lt;Attributes...&gt;, APIType, std::tuple&lt;Policies...&gt;&gt;
    【解决方案3】:

    创建一个嵌套类,每一层都有一个可变参数包。本质上:

    template<class... ArgsA> class Wrapper {
    public:
        template<class... ArgsB> class Type {
            //Here you have access to both template packs
            //...
        }
    }
    //Use like this:
    Wrapper<int, char, long unsigned int>::Type<float, double> a;
    //...
    

    在两个类之外定义Type的任何函数,但在头文件内,否则gcc会混淆。

    如需更完整的用例,请参阅my answer here

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-04-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多