更多 C++ 习语/enable-if
允许基于类型的任意属性进行函数重载
- 显式重载集管理
enable_if 模板族是一组工具,用于允许函数模板或类模板特化根据其模板参数的属性将自身包含或排除在匹配函数或特化集之外。例如,可以定义仅针对由特征类定义的任意类型集启用的函数模板,并且因此仅与之匹配。enable_if 模板也可以应用于启用类模板特化。enable_if 的应用在文献中进行了详细讨论。[1] [2]
C++ 中模板函数重载的合理操作依赖于 SFINAE (替换失败不是错误) 原则:[3] 如果在函数模板的实例化过程中形成了无效的参数或返回类型,则该实例化将从重载解析集移除,而不是导致编译错误。以下示例[1] 说明了为什么这一点很重要
int negate(int i) { return -i; }
template <class F>
typename F::result_type negate(const F& f) { return -f(); }
假设编译器遇到调用 negate(1)。第一个定义显然是更好的匹配,但编译器仍然必须考虑(并实例化原型)这两个定义才能找出这一点。用 F 作为 int 实例化第二个定义会导致
int::result_type negate(const int&);
其中返回类型无效。如果这是一个错误,添加一个无关的函数模板(从未被调用过)可能会破坏其他有效的代码。然而,由于 SFINAE 原则,上面的例子不是错误的。negate 的第二个定义只是从重载解析集移除。
enable_if 模板是用于控制创建 SFINAE 条件的工具。
enable_if 模板在语法上非常简单。它们总是成对出现:其中一个为空,另一个具有一个type
typedef,它转发其第二个类型参数。空结构会导致无效类型,因为它不包含任何成员。当编译时条件为假时,将选择空的enable_if
模板。追加::type
会导致无效的实例化,编译器会根据 SFINAE 原则将其丢弃。
template <bool, class T = void>
struct enable_if
{};
template <class T>
struct enable_if<true, T>
{
typedef T type;
};
以下是一个示例,展示了如何基于类型参数的任意属性在编译时选择重载的模板函数。假设函数 T foo(T t) 是为所有类型定义的,只要 T 是算术类型。enable_if 模板可以作为返回值类型使用,如本例所示
template <class T>
typename enable_if<is_arithmetic<T>::value, T>::type
foo(T t)
{
// ...
return t;
}
或者作为额外参数,如下所示
template <class T>
T foo(T, typename enable_if<is_arithmetic<T>::value >::type* = 0);
添加到 foo() 的额外参数被赋予默认值。由于 foo() 的调用者将忽略这个虚拟参数,因此它可以被赋予任何类型。特别是,我们可以允许它为 void *。考虑到这一点,我们可以简单地省略 enable_if 的第二个模板参数,这意味着当 is_arithmetic<T> 为真时,enable_if<...>::type 表达式将评估为 void。
是否将使能器编写为参数或在返回类型中很大程度上取决于个人喜好,但对于某些函数,只有一种选择是可行的
- 运算符具有固定数量的参数,因此 enable_if 必须在返回值类型中使用。
- 构造函数和析构函数没有返回值类型;额外参数是唯一的选择。
从 C++20 开始,requires
语句可以用于实现与 enable_if 相同的功能。与 enable_if 一样,如果使能器为 false
,则会关闭重载。
template <class T>
T foo(T) requires(is_arithmetic<T>::value);
Boost 库、C++ STL 等。
- ↑ a b Jaakko Järvi、Jeremiah Willcock、Howard Hinnant 和 Andrew Lumsdaine。基于类型的任意属性的函数重载。C/C++ 用户杂志,21(6):25-32,2003 年 6 月。
- ↑ Jaakko Järvi、Jeremiah Willcock 和 Andrew Lumsdaine。概念控制的多态性。在 Frank Pfennig 和 Yannis Smaragdakis 编著的《生成式编程和组件工程》,LNCS 卷 2830,第 228-244 页。施普林格出版社,2003 年 9 月。
- ↑ David Vandevoorde 和 Nicolai M. Josuttis。《C++ 模板:完整指南》。Addison-Wesley,2002 年。