CppGuide社区 CppGuide社区
首页
  • 🔥最新谷歌C++风格指南(含C++17/20)
  • 🔥C++17详解
  • 🔥C++20完全指南
  • 🔥C++23快速入门
🔥C++面试
  • 第1章 C++ 惯用法与Modern C++篇
  • 第2章 C++开发工具与调试进阶
  • 第3章 C++多线程编程从入门到进阶
  • 第4章 C++网络编程重难点解析
  • 第5章 网络通信故障排查常用命令
  • 第6章 网络通信协议设计
  • 第7章 高性能服务结构设计
  • 第8章 Redis网络通信模块源码分析
  • 第9章 服务其他模块设计
  • 🚀 全部章节.pdf 下载 (opens new window)
  • 🔥C++游戏编程入门(零基础学C++)
  • 🔥使用C++17从零开发一个调试器 (opens new window)
  • 🔥使用C++20从零构建一个完整的低延迟交易系统 (opens new window)
  • 🔥使用C++从零写一个C语言编译器 (opens new window)
  • 🔥从零用C语言写一个Redis
  • leveldb源码分析
  • libevent源码分析
  • Memcached源码分析
  • TeamTalk源码分析
  • 优质源码分享 (opens new window)
  • 🔥远程控制软件gh0st源码分析
  • 🔥Windows 10系统编程
  • 🔥Linux 5.x内核开发与调试 完全指南 (opens new window)
  • TCP源码实现超详细注释版.pdf (opens new window)
  • 高效Go并发编程
  • Go性能调优
  • Go项目架构设计
  • 🔥使用Go从零开发一个数据库
  • 🔥使用Go从零开发一个编译器 (opens new window)
  • 🔥使用Go从零开发一个解释器 (opens new window)
Rust编程指南
  • SQL零基础指南
  • MySQL开发与调试指南
GitHub (opens new window)
首页
  • 🔥最新谷歌C++风格指南(含C++17/20)
  • 🔥C++17详解
  • 🔥C++20完全指南
  • 🔥C++23快速入门
🔥C++面试
  • 第1章 C++ 惯用法与Modern C++篇
  • 第2章 C++开发工具与调试进阶
  • 第3章 C++多线程编程从入门到进阶
  • 第4章 C++网络编程重难点解析
  • 第5章 网络通信故障排查常用命令
  • 第6章 网络通信协议设计
  • 第7章 高性能服务结构设计
  • 第8章 Redis网络通信模块源码分析
  • 第9章 服务其他模块设计
  • 🚀 全部章节.pdf 下载 (opens new window)
  • 🔥C++游戏编程入门(零基础学C++)
  • 🔥使用C++17从零开发一个调试器 (opens new window)
  • 🔥使用C++20从零构建一个完整的低延迟交易系统 (opens new window)
  • 🔥使用C++从零写一个C语言编译器 (opens new window)
  • 🔥从零用C语言写一个Redis
  • leveldb源码分析
  • libevent源码分析
  • Memcached源码分析
  • TeamTalk源码分析
  • 优质源码分享 (opens new window)
  • 🔥远程控制软件gh0st源码分析
  • 🔥Windows 10系统编程
  • 🔥Linux 5.x内核开发与调试 完全指南 (opens new window)
  • TCP源码实现超详细注释版.pdf (opens new window)
  • 高效Go并发编程
  • Go性能调优
  • Go项目架构设计
  • 🔥使用Go从零开发一个数据库
  • 🔥使用Go从零开发一个编译器 (opens new window)
  • 🔥使用Go从零开发一个解释器 (opens new window)
Rust编程指南
  • SQL零基础指南
  • MySQL开发与调试指南
GitHub (opens new window)
  • C++20 完全指南 说明
  • 第1章 比较和<=>运算符
  • 第2章 函数参数的占位符类型
  • 第3章 概念、要求和约束
  • 第4章 概念、需求和约束详解
  • 第5章 标准概念详解
  • 第6章 范围与视图
  • 第7章 范围和视图的实用工具
  • 第8章 视图类型详解
  • 第9章 跨度(Spans)
  • 第10章 格式化输出
  • 第11章 <chrono>中的日期和时区
  • 第12章 std::jthread和停止令牌
  • 第13章 并发特性
  • 第14章 协程
  • 第15章 协程详解
  • 第16章 模块
  • 第17章 Lambda扩展
  • 第18章 编译期计算
  • 第19章 非类型模板参数(NTTP)扩展
  • 第20章 新的类型特性
    • 20.1 用于类型分类的新类型特性
      • 20.1.1 std::is_bounded_array_v<>和std::is_unbounded_array_v
    • 20.2 用于类型检查的新类型特性
      • 20.2.1 std::is_nothrow_convertible_v<>
    • 20.3 用于类型转换的新类型特性
      • 20.3.1 std::remove_cvref_t<>
      • 20.3.2 std::unwrap_reference<>和std::unwrap_ref_decay_t
      • 20.3.3 std::common_reference_t
      • 20.3.4 std::type_identity_t<>
    • 20.4 迭代器的新型特性
      • 20.4.1 iterdifferencet
      • 20.4.2 itervaluet
      • 20.4.3 iterreferencet
    • 20.5 用于布局兼容性的类型特性和函数
      • 20.5.1 islayoutcompatible_v
      • 20.5.2 ispointerinterconvertiblebaseof_v
      • 20.5.3 iscorrespondingmember()
      • 20.5.4 is_pointer_interconvertible_with_class()
    • 20.6 补充说明
  • 第21章 核心语言的小改进
  • 第22章 泛型编程的小改进
  • 第23章 C++标准库的小改进
  • 第24章 已弃用和移除的特性
  • cpp20completeguides
zhangxf
2025-03-20
目录

第20章 新的类型特性

# 第20章 新的类型特性

本章介绍C++20在其标准库中引入的几个类型特性(以及两个针对类型的底层函数)。

表20.1列出了C++20的这些新类型特性(所有特性都在std命名空间中定义)。

特性 作用
std::is_bounded_array_v<T>
std::is_unbounded_array_v<T>
std::is_nothrow_convertible_v<T, T2>
std::is_layout_compatible_v<T1, T2>
std::is_pointer_interconvertible_base_of_v<BaseT, DerT>
如果类型T是已知大小的数组类型,则返回true
如果类型T是未知大小的数组类型,则返回true
如果T可以不抛出异常地转换为类型T2,则返回true
如果T1与类型T2在布局上兼容,则返回true
如果指向DerT的指针可以安全地转换为指向其基类型BaseT的指针,则返回true
std::remove_cvref_t<T>
std::unwrap_reference_t<T>
std::unwrap_ref_decay_t<T>
std::common_reference_t<T...>
std::type_identity_t<T>
std::iter_difference_t<T>
std::iter_value_t<T>
std::iter_reference_t<T>
std::iter_rvalue_reference_t<T>
返回去掉引用、顶层const和volatile的类型T
如果T是std::reference_wrapper<>(由std::ref()或std::cref()创建),则返回其包裹的类型,否则返回T
如果T是std::reference_wrapper<>(由std::ref()或std::cref()创建),则返回其包裹的类型,否则返回T的退化类型
返回所有类型T...的公共类型(如果存在),可以向该类型赋值
返回类型T本身
返回可递增/迭代器类型T的差值类型
返回指针/迭代器类型T的值/元素类型
返回指针/迭代器类型T的引用类型
返回指针/迭代器类型T的右值引用类型
std::is_clock_v<T>
std::compare_three_way_result_t<T>
如果T是时钟类型,则返回true
返回使用<=>运算符比较两个值的结果类型
表20.1 新的类型特性

以下各节将详细讨论这些特性,但以下特性除外:

  • std::is_clock_v<>在关于新的<chrono>特性的章节中讨论。
  • std::compare_three_way_result_t<>在关于新的三路比较的章节中讨论。

注意,此外,类型特性std::is_pod<>在C++20中已被弃用。

# 20.1 用于类型分类的新类型特性

# 20.1.1 std::is_bounded_array_v<>和std::is_unbounded_array_v

std::is_bounded_array_v<T> std::is_unbounded_array_v<T>

用于判断类型T是否为有界/无界数组(大小已知/未知)。例如:

int  a[5];
std::is_bounded_array_v<decltype(a)>       // true
std::is_unbounded_array_v<decltype(a)>     // false

extern  int  b[];
std::is_bounded_array_v<decltype(b)>       // false
std::is_unbounded_array_v<decltype(b)>     // true
1
2
3
4
5
6
7

# 20.2 用于类型检查的新类型特性

# 20.2.1 std::is_nothrow_convertible_v<>

std::is_nothrow_convertible_v<From, To>

用于判断类型From是否可以保证不抛出任何异常地转换为类型To。例如:

// char* 转换为std::string:
std::is_convertible_v<char*,  std::string>                      // true
std::is_nothrow_convertible_v<char*,  std::string>              // false

// std::string转换为std::string_view:
std::is_convertible_v<std::string,  std::string_view>           // true
std::is_nothrow_convertible_v<std::string,  std::string_view>   // true
1
2
3
4
5
6
7

# 20.3 用于类型转换的新类型特性

# 20.3.1 std::remove_cvref_t<>

std::remove_cvref_t<T>

返回去掉引用、顶层const或volatile的类型T。表达式std::remove_cvref_t<T>等价于std::remove_cv_t<remove_reference_t<T>>。例如:

std::remove_cvref_t<const  std::string&>   // std::string
std::remove_cvref_t<const  char*  const>   // const char*
1
2

# 20.3.2 std::unwrap_reference<>和std::unwrap_ref_decay_t

std::unwrap_reference_t<T>

如果T是std::reference_wrapper<>(由std::ref()或std::cref()创建),则返回其包裹的类型,否则返回T。

std::unwrap_ref_decay_t<T>

如果T是std::reference_wrapper<>(由std::ref()或std::cref()创建),则返回其包裹的类型,否则返回T的退化类型。

例如:

std::unwrap_reference_t<decltype(std::ref(s))>     // std::string&
std::unwrap_reference_t<decltype(std::cref(s))>    // const  std::string&
std::unwrap_reference_t<decltype(s)>               // std::string
std::unwrap_reference_t<decltype(s)&>              // std::string&
std::unwrap_reference_t<int[4]>                    // int[4]

std::unwrap_ref_decay_t<decltype(std::ref(s))>     // std::string&
std::unwrap_ref_decay_t<decltype(std::cref(s))>    // const  std::string&
std::unwrap_ref_decay_t<decltype(s)>               // std::string
std::unwrap_ref_decay_t<decltype(s)&>              // std::string
std::unwrap_ref_decay_t<int[4]>                    // int*
1
2
3
4
5
6
7
8
9
10
11

# 20.3.3 std::common_reference<>_t

std::common_reference_t<T...>

返回所有类型T...的公共类型(如果存在),可以向该类型赋值。因此,对于给定的类型T1、T2和T3,该特性返回的类型应能接受这三种类型的值。理想情况下,它是一个引用类型。然而,如果涉及类型转换并创建了临时对象,那么它就是一个值类型。

例如:

std::common_reference_t<int&,  int>                                                       // int
std::common_reference_t<int&,  int&>                                                     // int&
std::common_reference_t<int&,  int&&>                                                   // const  int&
std::common_reference_t<int&&,  int&&>                                                 // int&&
std::common_reference_t<int&,  double>                                                 // double
std::common_reference_t<int&,  double&&>                                             // double
std::common_reference_t<char*,  std::string,  std::string_view> // std::string_view
std::common_reference_t<char,  std::string>                                       // ERROR
1
2
3
4
5
6
7
8

# 20.3.4 std::type_identity_t<>

std::type_identity_t<T>返回类型T本身。

这个类型特性有许多令人意想不到的用例:

  • 可以禁用使用参数来推导模板参数的功能。例如:
template<typename  T>
void  insert(std::vector<T>&  coll,  const  std::type_identity_t<T>&  value) {
    coll.push_back(value);
}

std::vector<double>  coll;
...
insert(coll,  42);  // 正确:42的类型不会用于推导类型T
1
2
3
4
5
6
7
8

如果参数value仅声明为const T&,编译器会报错,因为它会为类型T推导出两种不同的类型。

  • 可以将其用作构建块来定义返回类型的类型特性。例如,可以如下简单地定义一个去除const性的类型特性1:
template<typename  T>
struct  remove_const  :  std::type_identity<T>  { };

template<typename  T>
struct  remove_const<const  T>  :  std::type_identity<T>  { };
1
2
3
4
5

# 20.4 迭代器的新型特性

本节列出了迭代器基本类型函数部分中提到的迭代器特性。

# 20.4.1 iter_difference_t<>

std::iter_difference_t<T>会产生与可递增/迭代器类型T相对应的差值类型。

这个特性专门用于处理间接可读类型的两个对象的值类型。与传统的迭代器特性(std::iterator_traits<>)不同,这个特性能够正确处理新的迭代器类别。

请注意,不存在对应的名为std::iter_difference且带有type成员的数据结构。相反,这个类型特性是通过尝试使用新的辅助类型std::incrementable_traits<>的difference_type成员来定义的,其定义如下:

  • 如果有特化,就使用std::incrementable_traits<T>::difference_type。
  • 对于原始指针,使用std::ptrdiff_t。
  • 否则,如果已定义,使用T::difference_type。
  • 否则,使用两个T之间差值的有符号整数差值类型。
  • 对于const T,使用T的差值类型。

例如:

using  T1  =  std::iter_difference_t<int*>;                    // std::ptrdiff_t
using  T2  =  std::iter_difference_t<std::string>;             // std::ptrdiff_t
using  T3  =  std::iter_difference_t<std::vector<long>>;       // std::ptrdiff_t
using  T4  =  std::iter_difference_t<int>;                                             // int
using  T5  =  std::iter_difference_t<std::chrono::sys_seconds>;// 错误
1
2
3
4
5

# 20.4.2 iter_value_t<>

std::iter_value_t<T>会产生与指针/迭代器类型T相对应的非const值/元素类型。

这个特性专门用于处理间接可读类型的值类型。与传统的迭代器特性(std::iterator_traits<>)不同,这个特性能够正确处理新的迭代器类别。

请注意,不存在对应的名为std::iter_value且带有type成员的数据结构。相反,这个类型特性是通过尝试使用新的辅助类型std::indirectly_readable_traits<>的value_type成员来定义的,其定义如下:

  • 如果有特化,就使用std::indirectly_readable_traits<T>::value_type。
  • 对于原始指针,使用它所指向的非const/volatile类型。
  • 否则,如果已定义,使用remove_cv_t<T::value_type>。
  • 否则,如果已定义,使用remove_cv_t<T::element_type>。
  • 对于const T,使用T的值类型。

例如:

using  T1  =  std::iter_value_t<int*>;                  // int
using  T2  =  std::iter_value_t<const  int*  const>;    // int
using  T3  =  std::iter_value_t<std::string>;           // char
using  T4  =  std::iter_value_t<std::vector<long>>;     // long
using  T5  =  std::iter_value_t<int>;   				// 错误
1
2
3
4
5

# 20.4.3 iter_reference_t<>和iter_rvalue_reference_t<>

std::iter_reference_t<T>会产生与可解引用的指针/迭代器类型T相对应的左值引用类型。它等同于decltype(*declval<T&>())。

std::iter_rvalue_reference_t<T>会产生与可解引用的指针/迭代器类型T相对应的右值引用类型。它等同于decltype(std::ranges::iter_move(declval<T&>()))。

这些特性专门用于处理间接可写类型的值类型。与传统的迭代器特性(std::iterator_traits<>)不同,这些特性能够正确处理新的迭代器类别。

请注意,不存在对应的带有type成员的std::iter_value数据结构。相反,这些特性直接按照上述描述进行定义。

例如:

using  T1  =  std::iter_reference_t<int*>;                      // int&
using  T2  =  std::iter_reference_t<const  int*  const>;        // const  int&
using  T3  =  std::iter_reference_t<std::string>;               // 错误
using  T4  =  std::iter_reference_t<std::vector<long>>;         // 错误
using  T5  =  std::iter_reference_t<int>;   					// 错误
using  T6  =  std::iter_rvalue_reference_t<int*>;               // int&&
using  T7  =  std::iter_rvalue_reference_t<const  int*  const>; // const  int&&
using  T8  =  std::iter_rvalue_reference_t<std::string>;        // 错误
1
2
3
4
5
6
7
8

# 20.5 用于布局兼容性的类型特性和函数

在许多情况下,了解两种类型或指向类型的指针是否可以安全地相互转换非常重要。C++标准使用“布局兼容(layout-compatible)”这一术语来描述这种情况。

为了检查类成员之间的布局兼容关系,C++20还引入了两个新的普通函数。它们的优点是可以在运行时上下文中使用。

# 20.5.1 is_layout_compatible_v<>

std::is_layout_compatible_v<T1 , T2>会判断类型T1和T2是否布局兼容,这样你就可以使用reinterpret_cast安全地将指向它们的指针进行转换。

例如:

struct  Data  { 
    int  i;
    const  std::string  s; 
};

class  Type  { 
private:
    const  int  id  =  nextId();
    std::string  name;
public:
   ...
};

std::is_layout_compatible_v<Data,  Type> // true
1
2
3
4
5
6
7
8
9
10
11
12
13
14

请注意,对于布局兼容性而言,仅仅类型和位大致匹配是不够的。根据语言规则:

  • 有符号类型与无符号类型永远不会布局兼容。
  • char甚至与signed char和unsigned char都永远不会布局兼容。
  • 引用与非引用永远不会布局兼容。
  • 不同(即使布局兼容)类型的数组永远不会布局兼容。
  • 枚举类型与其底层类型永远不会布局兼容。

例如:

enum  class  E  {};
enum  class  F  :  int  {};

std::is_layout_compatible_v<E,  F>                    // true
std::is_layout_compatible_v<E[2],  F[2]>              // false
std::is_layout_compatible_v<E,  int>                  // false
std::is_layout_compatible_v<char ,  char>             // true
std::is_layout_compatible_v<char ,  signed  char>     // false
std::is_layout_compatible_v<char ,  unsigned  char>   // false
std::is_layout_compatible_v<char ,  char&>            // false
1
2
3
4
5
6
7
8
9
10

# 20.5.2 is_pointer_interconvertible_base_of_v<>

std::is_pointer_interconvertible_base_of<Base , Der>判断指向类型Der的指针是否可以使用reinterpret_cast安全地转换为指向其基类型Base的指针,如果可以则返回true。

如果两者具有相同的类型,这个特性总是返回true。例如:

struct  B1  {  };
struct  D1  :  B1  {  int  x;  };
struct  B2  {  int  x;  };
struct  D2  :  B2  {  int  y;  };   // 不是标准布局类型

std::is_pointer_interconvertible_base_of_v<B1,  D1>     // true
std::is_pointer_interconvertible_base_of_v<B2,  D2>     // false
1
2
3
4
5
6
7

指向D2的指针不能安全地转换为指向B2的指针,原因是D2不是标准布局类型,因为并非所有成员都在同一类中以相同的访问权限定义。

# 20.5.3 is_corresponding_member()

template<typename  S1,  typename  S2,  typename  M1,  typename  M2>
constexpr  bool  is_corresponding_member(M1  S1::*m1,  M2  S2::*m2)  noexcept ;
1
2

该函数判断m1和m2是否分别指向S1和S2中布局兼容的成员。这意味着这些成员以及它们前面的所有成员都必须布局兼容。当且仅当S1和S2是标准布局类型,M1和M2是对象类型,并且m1和m2不为空时,该函数才返回true。

例如:

struct  Point2D  {  int  a;  int  b;  };
struct  Point3D  {  int  x;  int  y;  int  z;  };
struct  Type1  {  const  int  id;  int  val;  std::string  name;  }; 
struct  Type2  {  unsigned  int  id;  int  val;  };

std::is_corresponding_member(&Point2D::b,  &Point3D::y)   // true
std::is_corresponding_member(&Point2D::b,  &Point3D::z)   // false (第二个与第三个int对比)
std::is_corresponding_member(&Point2D::b,  &Type1::val)   // true
std::is_corresponding_member(&Point2D::b,  &Type2::val)   // false (有符号与无符号对比)
1
2
3
4
5
6
7
8
9

# 20.5.4 is_pointer_interconvertible_with_class()

template<typename S, typename M>
constexpr bool is_pointer_interconvertible_with_class(M S::*m ) noexcept;
1
2

该函数用于判断类型为S的每个对象s是否与它的子对象s.*m指针可相互转换。当且仅当S是标准布局类型(standard-layout type),M是对象类型,并且m不为空时,函数返回true。

例如:

struct B1 { int x; };
struct B2 { int y; };
struct DB1 : B1 {};
struct DB1B2 : B1, B2 {};   // 不是标准布局类型

std::is_pointer_interconvertible_with_class<B1, int>(&DB1::x)          // true
std::is_pointer_interconvertible_with_class<DB1, int>(&B1::x)          // true
std::is_pointer_interconvertible_with_class<DB1, int>(&DB1::x)         // true
std::is_pointer_interconvertible_with_class<B1, int>(&DB1B2::x)        // true
std::is_pointer_interconvertible_with_class<DB1B2, int>(&B1::x)        // false
std::is_pointer_interconvertible_with_class<DB1B2, int>(&DB1B2::x)     // false
1
2
3
4
5
6
7
8
9
10
11

C++20标准在一条注释中解释了最后两个表达式为false的原因: 指向成员表达式&C::b的类型并不总是指向C的成员的指针。这在结合继承使用这些函数时可能会导致出人意料的结果:

struct A { int a; };
struct B { int b; };
struct C: public A, public B { };
struct A { int a; }; 				// 标准布局类
struct B { int b; }; 				// 标准布局类
struct C: public A, public B { }; 	// 不是标准布局类

std::is_pointer_interconvertible_with_class(&C::b)      // true
// true因为,尽管表面上如此,&C::b的类型是
// “指向B中int类型成员的指针”
std::is_pointer_interconvertible_with_class<C>(&C::b)   // false
// false因为它强制使用类C,从而失败
1
2
3
4
5
6
7
8
9

# 20.6 补充说明

类型特性is_bounded_array<>和is_unbounded_array<>按照沃尔特·E·布朗(Walter E. Brown)和格伦·J·费尔南德斯(Glen J. Fernandes)在http://wg21.link/p1357r1 (opens new window)中的提议被接受。

类型特性is_nothrow_convertible<>按照丹尼尔·克尔(Daniel Kr)在http://wg21.link/p0758r1 (opens new window)中的提议被接受。

类型特性common_reference<>作为范围库(ranges library)的一部分,由埃里克·尼布勒(Eric Niebler)、凯西·卡特(Casey Carter)和克里斯托弗·迪·贝拉(Christopher Di Bella)在http://wg21.link/p0896r4 (opens new window)中提议并被接受。

类型特性unwrap_reference<>和unwrap_ref_decay<>按照比森特·J·博特·埃斯克里瓦(Vicente J. Botet Escriba)在http://wg21.link/p0318r1 (opens new window)中的提议被接受。

类型特性remove_cvref<>按照沃尔特·E·布朗(Walter E. Brown)在http://wg21.link/p0550r2 (opens new window)中的提议被接受。

类型特性type_identity<>按照蒂穆尔·杜姆勒(Timur Doumler)在http://wg21.link/p0887r1 (opens new window)中的提议被接受。

迭代器类型特性作为采用范围库提议的一部分,由埃里克·尼布勒(Eric Niebler)、凯西·卡特(Casey Carter)和克里斯托弗·迪·贝拉(Christopher Di Bella)在http://wg21.link/p0896r4 (opens new window)中提议并被接受。

类型特性is_layout_compatible<>和is_pointer_interconvertible_base_of<>,以及函数is_corresponding_member()和is_pointer_interconvertible_with_class()按照丽莎·利平科特(Lisa Lippincott)在http://wg21.link/p0466r5 (opens new window)中的提议被接受。

上次更新: 2025/03/20, 19:44:38
第19章 非类型模板参数(NTTP)扩展
第21章 核心语言的小改进

← 第19章 非类型模板参数(NTTP)扩展 第21章 核心语言的小改进→

最近更新
01
C++语言面试问题集锦 目录与说明
03-27
02
第四章 Lambda函数
03-27
03
第二章 关键字static及其不同用法
03-27
更多文章>
Copyright © 2024-2025 沪ICP备2023015129号 张小方 版权所有
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式