目录
1. 说明
定义于头文件 <type_traits>:
| template< bool B, class T = void > | (since C++11) | |
如果 B 为真,则 std::enable_if 具有公共成员 typedef 类型,等于 T;否则,没有成员 typedef。此超越函数是利用 C++20 之前的概念的 SFINAE (Substitution Failure Is Not An Error)的一种便捷方式,特别是用于根据类型特征有条件地从候选集中删除函数,从而允许基于这些不同类型特征(trait)进行单独的函数重载或特化。
std::enable_if 可用于多种形式,包括:
• 作为附加函数参数(不适用于大多数运算符重载),
• 作为返回类型(不适用于构造函数和析构函数),
• 作为类模板或函数模板参数。
如果程序为 std::enable_if添加特化,则行为未定义。
2. 成员类型
2.1 类型定义
类型定义:
类型为 T 或“无”这样的成员,取决于B 的值。
辅助类型:
| template< bool B, class T = void > | (since C++14) |
可能的实现:
template<bool B, class T = void>
struct enable_if {};
template<class T>
struct enable_if<true, T> { typedef T type; };
2.2 注意事项
一个常见的错误是声明两个仅在默认模板参数方面不同的函数模板。这是行不通的,因为声明被视为对同一函数模板的重新声明(默认模板参数不考虑函数模板等价性)。
/* 以下错误 */
struct T
{
enum { int_t, float_t } type;
template<typename Integer,
typename = std::enable_if_t<std::is_integral<Integer>::value>>
T(Integer) : type(int_t) {}
template<typename Floating,
typename = std::enable_if_t<std::is_floating_point<Floating>::value>>
T(Floating) : type(float_t) {} // 错误: 视为重定义
};
/* 以下正确 */
struct T
{
enum { int_t, float_t } type;
template<typename Integer,
std::enable_if_t<std::is_integral<Integer>::value, bool> = true>
T(Integer) : type(int_t) {}
template<typename Floating,
std::enable_if_t<std::is_floating_point<Floating>::value, bool> = true>
T(Floating) : type(float_t) {} // OK
};
在命名空间范围函数模板的模板非类型参数类型中使用 enable_if 时应小心谨慎。某些 ABI 规范(如 Itanium ABI)(注:ABI—Application Binary Interface(应用程序二进制编程接口))未在混淆中包含非类型模板参数的实例化相关部分,这意味着两个不同函数模板的特化可能会以相同的混淆名称结束,并错误地链接在一起。例如:
// 第1个编译单元
struct X
{
enum { value1 = true, value2 = true };
};
template<class T, std::enable_if_t<T::value1, int> = 0>
void func() {} // #1
template void func<X>(); // #2
// 第2个编译单元
struct X
{
enum { value1 = true, value2 = true };
};
template<class T, std::enable_if_t<T::value2, int> = 0>
void func() {} // #3
template void func<X>(); // #4
函数模板 #1 和 #3 具有不同的签名,是不同的模板。尽管如此,#2 和 #4 尽管是不同函数模板的实例,但在 Itanium C++ ABI (_Z4funcI1XLi0EEvv) 中具有相同的混淆名称,这意味着链接器会错误地将它们视为同一实体。
3. 示例
#include <iostream>
#include <new>
#include <string>
#include <type_traits>
namespace detail
{
void* voidify(const volatile void* ptr) noexcept { return const_cast<void*>(ptr); }
}
// #1, 通过返回类型使能
template<class T>
typename std::enable_if<std::is_trivially_default_constructible<T>::value>::type
construct(T*)
{
std::cout << "default constructing trivially default constructible T\n";
}
// 与上述相同
template<class T>
typename std::enable_if<!std::is_trivially_default_constructible<T>::value>::type
construct(T* p)
{
std::cout << "default constructing non-trivially default constructible T\n";
::new(detail::voidify(p)) T;
}
// #2
template<class T, class... Args>
std::enable_if_t<std::is_constructible<T, Args&&...>::value> // 利用帮助类型
construct(T* p, Args&&... args)
{
std::cout << "constructing T with operation\n";
::new(detail::voidify(p)) T(static_cast<Args&&>(args)...);
}
// #3,通过一个参数使能
template<class T>
void destroy(
T*,
typename std::enable_if<
std::is_trivially_destructible<T>::value
>::type* = 0)
{
std::cout << "destroying trivially destructible T\n";
}
// #4, 通过非类型模板参数使能
template<class T,
typename std::enable_if<
!std::is_trivially_destructible<T>{} &&
(std::is_class<T>{} || std::is_union<T>{}),
bool>::type = true>
void destroy(T* t)
{
std::cout << "destroying non-trivially destructible T\n";
t->~T();
}
// #5, 通过模板参数使能
template<class T,
typename = std::enable_if_t<std::is_array<T>::value>>
void destroy(T* t) // note: function signature is unmodified
{
for (std::size_t i = 0; i < std::extent<T>::value; ++i)
destroy((*t)[i]);
}
/*
template<class T,
typename = std::enable_if_t<std::is_void<T>::value>>
void destroy(T* t) {} // error: has the same signature with #5
*/
// 通过一个模板参数使能A的部分特化
template<class T, class Enable = void>
class A {}; // 主模板
template<class T>
class A<T, typename std::enable_if<std::is_floating_point<T>::value>::type>
{}; // 对浮点特化
int main()
{
union { int i; char s[sizeof(std::string)]; } u;
construct(reinterpret_cast<int*>(&u));
destroy(reinterpret_cast<int*>(&u));
construct(reinterpret_cast<std::string*>(&u), "Hello");
destroy(reinterpret_cast<std::string*>(&u));
A<int>{}; // OK: 匹配主模板
A<double>{}; // OK: 区配部分特化
}
输出:
default constructing trivially default constructible T
destroying trivially destructible T
constructing T with operation
destroying non-trivially destructible T
510

被折叠的 条评论
为什么被折叠?



