C++ 对类模板实例化中的某些参数显式使用默认值

C++ 对类模板实例化中的某些参数显式使用默认值,c++,templates,c++11,default,C++,Templates,C++11,Default,类模板可以有多个都具有默认值的参数 template<typename UnderlyingT0 = int, typename UnderlyingtT1 = long, typename StringT = std::string> struct options; < C++ > 不,标准C++中没有任何东西可以实现这个。@FlorisVelleman在评论中提到的一个选项是引入别名模板: template <class UnderlyingT1, class Stri

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

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

< C++ >

不,标准C++中没有任何东西可以实现这个。@FlorisVelleman在评论中提到的一个选项是引入别名模板:

template <class UnderlyingT1, class StringT = std::string>
using options_defT0 = options<int, UnderlyingT1, StringT>;
在这里,缺点是1。您无法通过查看模板声明和2来判断默认参数<代码>选项和
选项
是不同的类型


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

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

template<typename UnderlyingT0 = int, typename UnderlyingtT1 = long, typename StringT = std::string>
struct options;
或者,可以将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>;
};
模板
结构选项{
模板
使用WithT0=选项;
模板
使用WithT1=选项;
模板
使用WithStringT=选项;
};
然后将其用作

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

如果所有模板参数都像示例中那样具有默认值,则可以创建一个helper结构来提取它们

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;
模板
用于辅助对象的结构默认\u;
模板
用于\u帮助程序的结构默认\u
{
使用type=std::tuple\u元素\u t;
};
模板
使用default\u for=typename default\u for\u helper::type;
然后像这样使用它:

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;
};
options<default_for<options,0>, int, std::string> o;
选项o;

我再次遇到这个问题,并提出了塞巴斯蒂安·雷德尔解决方案的更通用版本

//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;
//给定了要替换的索引、要替换的类型和要替换的元组
//返回与给定类型相同的元组,ReplaceAt的类型设置为ReplaceWith
模板
自动替换_类型(std::index_序列,std::tuple)
->std::tuple;
//使用元组中保存的类型实例化模板
模板
_元组中的结构类型_;
模板
结构类型\u来自\u元组
{
使用类型=T;
};
//将在索引ReplaTest中的模板实例化中使用的类型替换为ReplaceWith类型
模板
带n的结构;
模板
带\u n的结构
{
使用元组类型=decltype(替换类型
(std::index_sequence_表示{},std::tuple{});
使用type=typename type\u from\u tuple::type;
};
//方便别名
模板
使用with_n_t=typename with_n::type;
优点:

  • 灵活选择要更改的参数
  • 不需要更改原始类
  • 支持具有某些参数但没有默认值的类
  • 选项
    和带有
    是同一类型
一些用法示例:

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>
带有\u n\t a//选择权
与_n_t b//选择权

您可以进一步推广这一点,以获取索引和类型的可变对,这样您就不需要使用有限的\u options=options用\u n\u t
模板嵌套
可以满足您的要求吗?我不认为默认关键字在这个示例中被接受(尽管它是有意义的)。这是一个非常好的技巧,当然,缺点是当您只想更改最后一个参数时,它会变得混乱。但这本身就是一个非常有趣的概念+1在编译时使用fluent接口,这是第一次使用:)+1这很好。我现在正在编写一个boost库,所以我可能应该使用这种方法。另一方面,这对非类型模板参数不起作用,而Sebastian Redls idea可以。这个想法很酷,但除非我弄错了,否则Sebastian Redls解决方案已经提供了四个优点中的三个,除了允许原始类保持不变之外。但这确实意味着您只需要导入一个带有代码的头,并在任何需要的地方使用它,这很好。不过,我个人更喜欢语法
options::WithT0
,因为您实际使用的类是在语句的开头编写的,而不是在语句的结尾编写的。这似乎让代码更清晰了。是的,我只是觉得这是解决问题的另一个有趣的方法。您可以通过创建一个名为
DefaultsSettable
的mixin,然后让您的类从该类继承来继承功能,从而获得该语法。这样您就可以编写
options::with\n\u t::with\n\u t
//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;
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>