类型特性
类型特性定义一个编译时基于模板的结构,以查询或修改类型的属性。
试图特化定义于 <type_traits> 头文件的模板导致未定义行为,除了 std::common_type 可依照其所描述特化。
定义于<type_traits>头文件的模板可以用不完整类型实例化,除非另外有指定,尽管通常禁止以不完整类型实例化标准库模板。
杂项变换
根据编译期布尔常量隐藏一个函数重载或模板特化
std::enable_if
template< bool B, class T = void > |
(C++11 起) |
若 B
为 true ,则 std::enable_if
拥有等同于 T
的公开成员 typedef type
;否则,无该成员 typedef 。
此元函数是活用 SFINAE ,基于类型特性条件性地从重载决议移除函数,并对不同类型特性提供分离的函数重载与特化的便利方法。 std::enable_if
可用作额外的函数参数(不可应用于运算符重载)、返回类型(不可应用于构造函数与析构函数),或类模板或函数模板形参。
成员类型
类型 | 定义 |
type |
依赖 B 的值,为 T 或无此成员 |
辅助类型
template< bool B, class T = void > |
(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 <iostream>
#include <string>
namespace detail
{
struct inplace_t {};
}
void* operator new (std::size_t, void* p, detail::inplace_t)
{
return p;
}
// #1 ,通过返回类型使用
template<class T, class... Args>
typename std::enable_if < std::is_trivially_constructible < T, Args&&... >::value >::type
construct(T* t, Args&&... args)
{
std::cout << "constructing trivially constructible T\n";
}
// #3 ,通过形参启用
template<class T>
void destroy(T* 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<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<0, int, std::string>::type 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> a1; // OK ,匹配初等模板
A<double> a2; // OK ,匹配部分特化
}
输出
constructing trivially constructible T
destroying trivially destructible T