类型特性
类型特性定义一个编译时基于模板的结构,以查询或修改类型的属性。
试图特化定义于 <type_traits> 头文件的模板导致未定义行为,除了 std::common_type 可依照其所描述特化。
定义于<type_traits>头文件的模板可以用不完整类型实例化,除非另外有指定,尽管通常禁止以不完整类型实例化标准库模板。
杂项变换
void 变参别名模板
std::void_t
template< class... > |
(C++17 起) |
将任意类型的序列映射到类型 void 的工具元函数。
注意
模板元编程中,用此元函数检测 SFINAE 语境中的病式类型:
// 初等模板处理无嵌套 ::type 成员的类型:
template< class, class = std::void_t<> >
struct has_type_member : std::false_type { };
// 特化识别拥有嵌套 ::type 成员的类型:
template< class T >
struct has_type_member<T, std::void_t<typename T::type>> : std::true_type { };
它亦可用于检测表达式的合法性:
// 初等模板处理不支持前置自增的类型:
template< class, class = std::void_t<> >
struct has_pre_increment_member : std::false_type { };
// 特化识别支持前置自增的类型:
template< class T >
struct has_pre_increment_member<T,
std::void_t<decltype( ++std::declval<T&>() )>
> : std::true_type { };
直至 CWG1558 ( C++14 缺陷)前,别名模板中未使用的参数不保证 SFINAE 且可被忽略,故早期编译器要求 void_t
的更复杂定义,例如:
template<typename... Ts> struct make_void { typedef void type;};
template<typename... Ts> using void_t = typename make_void<Ts...>::type;
调用示例
#include <iostream>
#include <type_traits>
#include <vector>
#include <map>
namespace std
{
template<typename... Ts> struct make_void
{
typedef void type;
};
template<typename... Ts> using void_t = typename make_void<Ts...>::type;
}
class A {};
template <typename T, typename = void>
struct is_iterable : std::false_type {};
template <typename T>
struct is_iterable<T, std::void_t<decltype(std::declval<T>().begin()),
decltype(std::declval<T>().end())>>
: std::true_type {};
// 迭代器特性,其 value_type 始终为被迭代容器的 value_type ,
// 即使用 value_type 为 void 的 back_insert_iterator
template <typename T, typename = void>
struct iterator_trait: std::iterator_traits<T> {};
template <typename T>
struct iterator_trait<T, std::void_t<typename T::container_type>>
: std::iterator_traits<typename T::container_type::iterator> {};
int main()
{
std::cout << std::boolalpha;
std::cout << "is_iterable<std::vector<double>>::value: "
<< is_iterable<std::vector<double>>::value << std::endl;
std::cout << "is_iterable<std::map<int, double>>::value: "
<< is_iterable<std::map<int, double>>::value << std::endl;
std::cout << "is_iterable<double>::value: "
<< is_iterable<double>::value << std::endl;
std::cout << "is_iterable<A>::value: "
<< is_iterable<A>::value << std::endl;
std::vector<int> v;
std::cout << "std::is_same<iterator_trait<std::back_inserter(v)>,iterator_trait<v.cbegin()>: "
<< std::is_same<iterator_trait<decltype(std::back_inserter(v))>::value_type
, iterator_trait<decltype(v.cbegin())>::value_type >::value << std::endl;
return 0;
}
输出
is_iterable<std::vector<double>>::value: true
is_iterable<std::map<int, double>>::value: true
is_iterable<double>::value: false
is_iterable<A>::value: false
std::is_same<iterator_trait<std::back_inserter(v)>,iterator_trait<v.cbegin()>: true