C++ 参考手册
- C++11
- C++14
- C++17
- C++20
- C++ 编译器支持情况表
- 独立与宿主实现
- C++ 语言
- C++ 关键词
- 预处理器
- C++ 标准库头文件
- 具名要求
- 功能特性测试 (C++20)
- 工具库
- 类型支持(基本类型、RTTI、类型特性)
- std::result_of, std::invoke_result
- std::integral_constant
- std::is_constant_evaluated
- std::max_align_t
- offsetof
- NULL
- std::bad_typeid
- std::bad_cast
- std::numeric_limits
- std::type_info
- std::ptrdiff_t
- std::byte
- std::conjunction
- std::disjunction
- std::negation
- std::is_swappable_with, std::is_swappable, std::is_nothrow_swappable_with, std::is_nothrow_swappable
- std::is_invocable, std::is_invocable_r, std::is_nothrow_invocable, std::is_nothrow_invocable_r
- std::is_aggregate
- std::has_unique_object_representations
- std::endian
- std::remove_cvref
- std::type_index
- C 数值极限接口
- 定宽整数类型 (C++11 起)
- std::is_bounded_array
- std::is_unbounded_array
- std::size_t
- std::nullptr_t
- std::is_integral
- std::rank
- std::is_void
- std::is_null_pointer
- std::is_array
- std::is_pointer
- std::is_enum
- std::is_union
- std::is_class
- std::is_function
- std::is_object
- std::is_scalar
- std::is_compound
- std::is_floating_point
- std::is_fundamental
- std::is_arithmetic
- std::is_reference
- std::is_lvalue_reference
- std::is_rvalue_reference
- std::is_member_pointer
- std::is_member_object_pointer
- std::is_member_function_pointer
- std::is_const
- std::is_volatile
- std::is_empty
- std::is_polymorphic
- std::is_final
- std::is_abstract
- std::is_trivial
- std::is_trivially_copyable
- std::is_standard_layout
- std::is_literal_type
- std::is_pod
- std::is_signed
- std::is_unsigned
- std::is_constructible, std::is_trivially_constructible, std::is_nothrow_constructible
- std::is_default_constructible, std::is_trivially_default_constructible, std::is_nothrow_default_constructible
- std::is_copy_constructible, std::is_trivially_copy_constructible, std::is_nothrow_copy_constructible
- std::is_move_constructible, std::is_trivially_move_constructible, std::is_nothrow_move_constructible
- std::is_assignable, std::is_trivially_assignable, std::is_nothrow_assignable
- std::is_copy_assignable, std::is_trivially_copy_assignable, std::is_nothrow_copy_assignable
- std::is_move_assignable, std::is_trivially_move_assignable, std::is_nothrow_move_assignable
- std::is_destructible, std::is_trivially_destructible, std::is_nothrow_destructible
- std::has_virtual_destructor
- std::is_same
- std::is_base_of
- std::is_convertible, std::is_nothrow_convertible
- std::is_layout_compatible
- std::is_pointer_interconvertible_base_of
- std::is_pointer_interconvertible_with_class
- std::is_corresponding_member
- std::alignment_of
- std::extent
- std::remove_cv, std::remove_const, std::remove_volatile
- std::add_cv, std::add_const, std::add_volatile
- std::make_signed
- std::make_unsigned
- std::remove_reference
- std::add_lvalue_reference, std::add_rvalue_reference
- std::remove_pointer
- std::add_pointer
- std::remove_extent
- std::remove_all_extents
- std::aligned_storage
- std::aligned_union
- std::decay
- std::enable_if
- std::void_t
- std::conditional
- std::common_type
- std::common_reference
- std::underlying_type
- std::type_identity
- 注释
- 概念库 (C++20)
- 错误处理
- 动态内存管理
- 日期和时间工具
- 字符串库
- 容器库
- 迭代器库
- 范围库 (C++20)
- 算法库
- 数值库
- 输入/输出库
- 文件系统库
- 本地化库
- 正则表达式库
- 原子操作库
- 线程支持库
- 实验性 C++ 特性
- 有用的资源
- 索引
- std 符号索引
- 协程支持 (C++20)
- C++ 关键词
位置:首页 > C++ 参考手册 >类型支持(基本类型、RTTI、类型特性) > std::enable_if
std::enable_if
若 B
为 true ,则 std::enable_if
拥有等同于 T
的公开成员 typedef type
;否则,无该成员 typedef 。
此元函数是活用 SFINAE ,基于类型特性条件性地从重载决议移除函数,并对不同类型特性提供分离的函数重载与特化的便利方法。 std::enable_if
可用作额外的函数参数(不可应用于运算符重载)、返回类型(不可应用于构造函数与析构函数),或类模板或函数模板形参。
添加 enable_if
的特化的程序行为未定义。
成员类型
类型 | 定义 |
type
|
依赖 B 的值,为 T 或无此成员
|
辅助类型
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; }; |
注解
常见错误是声明二个函数模板,而它们仅于其默认模板实参相异。这是无效的,因为这些函数声明被当做同一函数模板的再声明(默认模板实参不为函数模板等价所考虑)。
/* WRONG */ struct T { enum { int_t,float_t } m_type; template <typename Integer, typename = std::enable_if_t<std::is_integral_v<Integer>> > T(Integer) : m_type(int_t) {} template <typename Floating, typename = std::enable_if_t<std::is_floating_point_v<Floating>e> > T(Floating) : m_type(float_t) {} // error: cannot overload }; /* RIGHT */ struct T { enum { int_t,float_t } m_type; template <typename Integer, std::enable_if_t<std::is_integral_v<Integer>, int> = 0 > T(Integer) : m_type(int_t) {} template <typename Floating, std::enable_if_t<std::is_floating_point_v<Floating>, int> = 0 > T(Floating) : m_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 <type_traits> #include <new> #include <iostream> #include <string> 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> // Using helper type 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>{}), int>::type = 0> 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) // 注:不修改函数签名 { 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() { std::aligned_union_t<0,int,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
参阅
(C++17) |
void 变参别名模板 (别名模板) |