在类模板实例化中明确使用某些参数的默认值

时间:2015-04-17 08:39:40

标签: c++ templates c++11 default

类模板可以有多个参数都具有默认值。

template<typename UnderlyingT0 = int, typename UnderlyingtT1 = long, typename StringT = std::string>
struct options;

使用默认参数设置模板很简单:

options<> my_default_options;

但是,如果我想更改参数子集呢?

options<int, int, std::wstring> wstring_options;

int是第一个参数的默认值并不明显,而第二个参数则不是。有什么像

options<default, int, std::wstring> wstring_options;

在C ++中?

4 个答案:

答案 0 :(得分:19)

不,标准C ++中没有任何内容可以实现这一点。 @FlorisVelleman在评论中指出的一个选择是引入一个别名模板:

template <class UnderlyingT1, class StringT = std::string>
using options_defT0 = options<int, UnderlyingT1, StringT>;

这样做的缺点是必须在别名定义中明确复制UnderlyingT0的默认参数,但至少它是&#39;仅在一个地方重复。

许多Boost库使用替代选项。他们引入了一个特殊标记use_default,并使 成为默认值。像这样:

struct use_default {};

template<typename UnderlyingT0 = use_default, typename UnderlyingtT1 = use_default, typename StringT = use_default>
struct options
{
  using RealUnderlyingT0 = typename std::conditional<
    std::is_same<UnderlyingT0, use_default>::value,
    int,
    UnderlyingT0
  >::type;

  using RealUnderlyingT1 = typename std::conditional<
    std::is_same<UnderlyingT1, use_default>::value,
    long,
    UnderlyingT1
  >::type;

  using RealStringT = typename std::conditional<
    std::is_same<StringT, use_default>::value,
    std::string,
    StringT
  >::type;
};

这里的缺点是1.你不能通过查看模板声明告诉默认参数,而options<>options<int, long, std::string>是不同的类型。

前者可以通过良好的文档来修复,后者可以通过明智地使用转换函数和基类来帮助。

答案 1 :(得分:14)

没有办法直接重用默认参数。您可以使用Floris的注释作为为常见用途提供缩写的方法,但模板别名仍将重复默认值。

或者,可以设置options struct以允许切换参数:

template <typename UnderlyingT0 = int,
          typename UnderlyingT1 = long,
          typename StringT = std::string>
struct options {
  template <typename NewT0>
  using WithT0 = options<NewT0, UnderylingT1, StringT>;
  template <typename NewT1>
  using WithT1 = options<UnderylingT0, NewT1, StringT>;
  template <typename NewStringT>
  using WithStringT = options<UnderylingT0, UnderylingT1, NewStringT>;
};

然后将其用作

options<>::WithT1<int>::WithStringT<std::wstring>

答案 2 :(得分:6)

如果您的所有模板参数都有默认值,例如,您可以创建一个帮助程序结构来为您提取它们。

template <class T, size_t N>
struct default_for_helper;

template <template <typename...> class T, size_t N, typename... Args>
struct default_for_helper<T<Args...>, N>
{
    using type = std::tuple_element_t<N, std::tuple<Args...>>;
};

template <template <typename...> class T, size_t N>
using default_for = typename default_for_helper<T<>, N>::type;

然后像这样使用它:

options<default_for<options,0>, int, std::string> o;

答案 3 :(得分:1)

我再次遇到了这个问题,并提出了更为通用的Sebastian Redl解决方案。

//given an index to replace at, a type to replace with and a tuple to replace in
//return a tuple of the same type as given, with the type at ReplaceAt set to ReplaceWith
template <size_t ReplaceAt, typename ReplaceWith, size_t... Idxs, typename... Args>
auto replace_type (std::index_sequence<Idxs...>, std::tuple<Args...>)
    -> std::tuple<std::conditional_t<ReplaceAt==Idxs, ReplaceWith, Args>...>;

//instantiates a template with the types held in a tuple
template <template <typename...> class T, typename Tuple>
struct type_from_tuple;

template <template <typename...> class T, typename... Ts>
struct type_from_tuple<T, std::tuple<Ts...>>
{
    using type = T<Ts...>;
};

//replaces the type used in a template instantiation of In at index ReplateAt with the type ReplaceWith
template <size_t ReplaceAt, typename ReplaceWith, class In>
struct with_n;

template <size_t At, typename With, template <typename...> class In, typename... InArgs>
struct with_n<At, With, In<InArgs...>>
{
    using tuple_type = decltype(replace_type<At,With>
        (std::index_sequence_for<InArgs...>{}, std::tuple<InArgs...>{}));

    using type = typename type_from_tuple<In,tuple_type>::type;
};

//convenience alias
template <size_t ReplaceAt, typename ReplaceWith, class In>
using with_n_t = typename with_n<ReplaceAt, ReplaceWith, In>::type;

优点:

  • 灵活选择要更改的参数
  • 不需要更改原始课程
  • 支持具有一些参数但没有默认值的类
  • options<int,long,int>with_n_t<2,int,options<>>属于同一类型

一些用法示例:

with_n_t<1, int, options<>> a; //options<int, int, std::string>
with_n_t<2, int,
   with_n_t<1, int, options<>>> b; //options<int, int, int>

您可以进一步概括这一点,以获取可变对的索引和类型,这样您就不需要嵌套with_n_t