如何封装/隐藏可变参数模板参数

时间:2016-10-27 06:55:35

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

考虑

template<
          typename A, 
          template<typename, typename...> class B,
          typename C = default_type,
          typename ... D 
        > struct D : B<A,D...> 
{ /*text*/ };

现在当可变参数D不为空/无效时,即使需要C,也必须明确定义参数default_type

我试图避免这种情况,即我只想在C时明确定义default_type

我在考虑像

这样的东西
template<
          typename A, 
          template<typename, typename...> class B,
          typename EncapsulatedVariadicD,              
          typename C = default_type              
        > struct D : B<A,EncapsulatedVariadicD::D...> 
{ /*text*/   };

但不确定是否可行,如果是,那么如何定义EncapsulatedVariadicD

欢迎任何替代/工作解决方案。

2 个答案:

答案 0 :(得分:3)

通常,当您需要封装类型列表时,可以使用std::tuple

你可以这样:

template <
  template <typename, typename...> class B,
  typename A,
  typename Pack
>
struct Base;

template <
  template <typename, typename...> class B,
  typename A,
  typename... D
>
struct Base<B, A, std::tuple<D...>>
{
  using type = B<A, D...>;
};

template<
  typename A,
  template<typename, typename...> class B,
  typename PackedD,
  typename C = default_type
>
struct D : Base<B, A, PackedD>::type
{ /*text*/ };

并像这样使用它:

D<SomeB, std::tuple<D1, D2>>

引用您的评论:

  

如果C不是默认值且PackedD为空,则必须将其明确提供为std::tuple<>,否则可能是此参数的默认值。我猜我不能两种方式,这是真的吗?

如果您不介意您的模板在内部变得更加复杂,那么可以双向拥有它。以下是:

// Unique type, internal marker for "use default template argument"
struct UseDefault {};

// GoalChooser has internal `type` which will hold the actual definition
template <
  typename A,
  template <typename, typename...> class B,
  typename Arg1,
  typename Arg2
>
struct GoalChooser;

template <
  typename A,
  template <typename, typename...> class B,
  typename C,
  typename... D
>
struct GoalChooser<A, B, C, std::tuple<D...>>
{
  struct type : B<A, D...>
  {
    // Your original definition of class template D goes here
  };
};

template <
  typename A,
  template <typename, typename...> class B,
  typename C,
  typename... D
>
struct GoalChooser<A, B, std::tuple<D...>, C>
{
  using type = typename GoalChooser<A, B, C, std::tuple<D...>>::type;
};


// GetArg applies appropriate default template arguments as applicable
template <
  typename Arg1,
  typename Arg2
>
struct GetArg
{
  using arg1 = Arg1;
  using arg2 = Arg2;
};

template <
  typename Arg
>
struct GetArg<UseDefault, Arg> : GetArg<Arg, UseDefault> {};

template <
  typename... T
>
struct GetArg<std::tuple<T...>, UseDefault>
{
  using arg1 = std::tuple<T...>;
  using arg2 = default_type;
};

template <
  typename C
>
struct GetArg<C, UseDefault>
{
  using arg1 = C;
  using arg2 = std::tuple<>;
};

template <>
struct GetArg<UseDefault, UseDefault>
{
  using arg1 = default_type;
  using arg2 = std::tuple<>;
};


// Client code will use this
template <
  typename A,
  template <typename, typename...> class B,
  typename Arg1 = UseDefault,
  typename Arg2 = UseDefault
>
using D = typename GoalChooser<
  A,
  B,
  typename GetArg<Arg1, Arg2>::arg1,
  typename GetArg<Arg1, Arg2>::arg2,
>::type;

我们的想法是客户端代码使用D(现在已成为别名模板)并且可以传递std::tuple(表示其D...参数)或其他类型(指示它的C参数),或者两者都没有,或者两者都是(按任意顺序,偶数)。

适当的默认参数的应用由GetArg完成,然后由GoalChooser处理别名的解析。

这假设C的值永远不会是std::tuple。如果这不是可接受的限制,则可以krzaq's answer的精神使用自定义类型而不是std::tuple来规避它。

(请注意,此解决方案不再需要我的第一个答案中的Base帮助程序。)

答案 1 :(得分:3)

使用打包类型,我们称之为type_list。如果您不需要关心保留实例,也可以使用std::tuple

template<typename... Ts>
struct type_list{};

然后专门为您设计模板:

template<typename A, template<typename, typename...> class B,
        typename TypeList, typename C = default_type>
struct D;

template<typename A, template<typename, typename...> class B,
         typename... TypesInTheList, typename C>
struct D<A,B,type_list<TypesInTheList...>,C> : B<A, TypesInTheList...>
{
    using parent = B<A, TypesInTheList...>;
};

您可以按预期使用它:

D<int, some_b, type_list<float, char, double>>

live demo