显式使用类模板实例化中某些参数的默认值
Explicitly use defaults for some parameters in class template instantiation
类模板可以有多个参数,这些参数都具有默认值。
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++?
不,标准C++中没有任何内容可以实现这一点。@FlorisVelleman在评论中指出的一种选择是引入别名模板:
template <class UnderlyingT1, class StringT = std::string>
using options_defT0 = options<int, UnderlyingT1, StringT>;
这样做的缺点是必须在别名定义中显式复制默认参数 UnderlyingT0
,但至少它只在一个地方重复。
许多 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. 您无法通过查看模板声明来判断默认参数,以及 2. options<>
和options<int, long, std::string>
是不同的类型。
前者可以通过良好的文档来修复,而后者可以通过明智地使用转换函数和基类来提供帮助。
没有办法直接重用默认参数。您可以使用 Floris 的注释作为提供常见用法的速记的一种方式,但模板别名仍将重复默认值。
或者,可以设置选项结构以允许切换参数:
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>
如果所有模板参数都有默认值(如示例中所示),则可以创建一个帮助程序结构来为您提取它们。
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;
我再次遇到这个问题,并提出了塞巴斯蒂安·雷德尔解决方案的更通用版本。
//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
。
相关文章:
- 当给定默认值时,为什么此模板参数推导失败
- 如何在C++中提供模板化函数作为另一个函数的参数,默认值?
- 在 c++ 中,如果我创建一个接受一个具有默认值的参数的构造函数 - 它会用作默认(空)构造函数吗?
- 为什么无法在可变参数模板中将尾随模板参数指定为默认值?
- 可变参数模板和具有继承的默认值
- 具有默认值的单个参数构造函数是否与默认构造函数相同?
- 在子类函数覆盖中省略具有默认值的参数
- 函数作为具有默认值的参数
- 具有另一个具有默认值的模板参数的模板推导
- const变量是否可以在具有默认值的参数中赋值(作为可选参数)
- 可以通过常量引用返回默认参数的值吗?
- 对 const 引用参数使用默认值会导致崩溃
- C ,构造器中允许的参考参数默认值
- 是否可以设计一个包含模板参数默认值的类
- 构造函数中参数C++默认值
- 如何设置依赖于其他参数的参数默认值
- QInput对话框参数默认值
- 在函数定义中指定参数默认值会导致错误 C2143:语法错误:'='之前缺少')'
- 内置类型的模板参数默认值
- 是否可以使用构造函数或对象作为其他类方法的参数默认值?