std::enable_if
来自cppreference.com
<tbody>
</tbody>
| 在标头 <type_traits> 定义
|
||
template< bool B, class T = void > struct enable_if; |
(C++11 起) | |
若 B 为 true,则 std::enable_if 拥有等同于 T 的公开成员 typedef type;否则,无成员 typedef。
此元函数是在 C++20 的概念可用前,活用 SFINAE 的便利方法,尤其是基于类型特征从候选集中条件性地移除函数,并对不同类型特征提供分离的函数重载与特化。
std::enable_if 有多种用法,包括:
- 用作额外的函数实参(不适用于大多数运算符重载)
- 用作返回类型(不适用于构造函数与析构函数)
- 用作类模板或函数模板形参。
如果程序添加了 std::enable_if 的特化,那么行为未定义。
成员类型
| 类型 | 定义 |
type
|
为 T 或无此成员,取决于 B 的值
|
辅助类型
<tbody> </tbody> template< bool B, class T = void > using enable_if_t = typename enable_if<B,T>::type; |
(C++14 起) | |
可能的实现
template<bool B, class T = void>
struct enable_if {};
template<class T>
struct enable_if<true, T> { typedef T type; };
|
注解
一种常见错误是声明两个仅在默认模板实参上有别的函数模板。这样做是无效的,因为这些函数声明被当做同一函数模板的再声明(函数模板等价性中并不计入默认模板实参)。
/* 错误 */
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,名字重整中并不包含非类型模板形参的实例化依赖部分。这表示两个不同的函数模板特化可能最后得到相同的重整名,并且错误地相互链接。例如:
// 第一个翻译单元
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
// 第二个翻译单元
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),这表示链接器会错误地认为它们是同一实体。
示例
运行此代码
#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 << "默认构造可平凡默认构造的 T\n";
}
// 同上
template<class T>
typename std::enable_if<!std::is_trivially_default_constructible<T>::value>::type
construct(T* p)
{
std::cout << "默认构造不可平凡默认构造的 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 << "带操作构造 T\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 << "销毁可平凡析构的 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 << "销毁不可平凡析构的 T\n";
t->~T();
}
// #5,经由类型模板形参启用
template<class T,
typename = std::enable_if_t<std::is_array<T>::value>>
void destroy(T* t) // 注:不修改函数签名
{
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){} // 错误:函数签名与 #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:匹配偏特化
}
输出:
默认构造可平凡默认构造的 T
销毁可平凡析构的 T
带操作构造 T
销毁不可平凡析构的 T
参阅
(C++17) |
void 变参别名模板 (别名模板) |