C++ Templates (1.4 默认模板实参 Default Template Arguments)
1.4 默认模板实参 Default Template Arguments
可以为模板参数定义默认值,这些值被称为默认模板实参(default template arguments),并且可以用于任何类型的模板[1]
比如:当需要组合不同的方法来定义返回类型使其具有不同参数类型的能力(如前一节所述),可以引入模板参数RT作为返回类型,并使RT成为两个调用实参的共同类型作为默认值。同样,有多个选择:
- 直接使用运算operator ? :。然而,由于不得不在调用参数a和b声明之前使用运算operator ? :,因此仅仅可以使用他们的类型:
basics/maxdefault1.hpp
#include <type_traits>
template <typename T1, typename T2,
typename RT = std::decay_t<decltype(true ? T1() : T2())>>
RT max(T1 a, T2 b)
{
return b < a ? a : b;
}
注意到此处使用了std::decay_t<> 用于确保不会返回引用[2]。
此实现需要传入参数的默认构造函数能够被调用。这里有另外一个解决方案,使用std::declval,但这使得声明更加复杂。第11.2.3中参考另一个例子。
- 使用类型属性中的std::common_type<>来指定返回类型的默认值:
// basics/maxdefault3.hpp
#include <type_traits>
template <typename T1, typename T2,
typename RT = std::common_type_t<T1, T2>>
RT max(T1 a, T2 b)
{
return b < a ? a : b;
}
同样请注意std::common_type<>会发生退化,因此返回值不会是一个引用。在任何情况下,调用者可以使用返回类型的默认值:
auto a = max(4, 7.2);
或者在显式指定其他模板实参类型后指定返回类型:
auto b = ::max<double, int, long double>(7.2, 4);
然而,这又有新的问题:仅仅为了指定返回类型,这需要指定3个类型。另一种替代方法需要这样一种能力:将返回类型作为第一个模板参数,但依然能够从其他模板实参中推断出该模板参数类型。理论上,前面的模板参数有默认实参而后面的模板参数没有默认实参:
template <typename RT = long, typename T1, typename T2>
RT max(T1 a, T2 b)
{
return b < a ? a : b;
}
使用该定义,可以如此调用:
int i;
long l;
...
max(i, l); // 返回long(模板参数的默认实参作为返回类型)
max<int>(4, 42); // 显式指定返回int类型
然而,这种方法只有在当模板参数具有自然的默认值的情况下才有意义(this approach only make sense, if there is a "natural" default for a template parameter)。但此处,模板参数的默认实参需要依赖于之前的模板参数。理论上,这是可能的,正如第26.5.1中所讨论的,但这依赖于类型特性,并使得定义变得复杂。
基于以上原因,最好的、最简单的解决方案便是让编译器推断返回类型,如1.3.2节所述。
脚注