@vivekgalatage: 迟到总比不到好。我知道C++26已经全面推出,但许多功能的基础版本源自……

X AI KOLs Timeline 新闻

摘要

所有C++20核心语言功能的详细概述及示例,作为速查表。

迟到总比不到好。我知道C++26已经全面推出,但许多功能的基础版本源自C++20。https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html…
查看原文
查看缓存全文

缓存时间: 2026/07/09 23:52

迟做总比不做好。我知道 C++26 已经来临,但很多特性都有源自 C++20 的基础版本。https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html…


所有 C++20 核心语言特性及示例

来源:https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html

引言

这篇文章背后的故事非常简单:我想了解新的 C++20 语言特性,并在一页上对所有特性做一个简要总结。于是,我决定阅读所有提案,并创建这个“速查表”,解释和演示每个特性。这不是一篇关于“最佳实践”的文章,仅用于演示目的。大多数示例灵感来自或直接取自相应的提案,所有功劳归功于它们的作者以及 ISO C++ 委员会的成员们。尽情阅读吧!

目录

  • 概念(Concepts)(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#concepts)
  • 模块(Modules)(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#modules)
  • 协程(Coroutines)(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#coroutines)
  • 三路比较(Three-way comparison)(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#three-way-comparison)
  • Lambda 表达式(Lambda expressions)(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#lambda-features)
    • 允许 lambda 捕获 [=, this]
    • 泛型 lambda 的模板参数列表(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#lambda-templ-params)
    • 未求值上下文中的 lambda(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#lambda-uneval-ctx)
    • 默认可构造和可赋值的无状态 lambda(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#lambda-def-ctor)
    • lambda 初始化捕获中的包展开(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#lambda-pack-exp)
  • 常量表达式(Constant expressions)(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#constexpr-features)
    • 立即函数(consteval)(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#consteval)
    • constexpr 虚函数(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#constexpr-virtual)
    • constexpr try-catch 块(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#constexpr-try-catch)
    • constexpr dynamic_cast 和多态 typeid(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#constexpr-dyn-cast)
    • constexpr 中更改 union 的活动成员(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#constexpr-union)
    • constexpr 分配(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#constexpr-alloc)
    • constexpr 函数中的平凡默认初始化(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#constexpr-trivial-def-init)
    • constexpr 函数中的未求值 asm 声明(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#constexpr-asm)
    • std::is_constant_evaluated()(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#is-const-eval)
  • 聚合体(Aggregates)(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#aggregates)
    • 禁止带有用户声明构造函数的聚合体(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#aggr-no-ctor)
    • 聚合体的类模板参数推导(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#ctad-aggr)
    • 聚合体的括号初始化(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#aggr-paren-init)
  • 非类型模板参数(Non-type template parameters)(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#nttp)
    • 非类型模板参数中的类类型(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#class-types-nttp)
    • 广义非类型模板参数(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#nttp-gen)
  • 结构化绑定(Structured bindings)(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#struct-bindings)
    • 结构化绑定的 lambda 捕获和存储类说明符(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#structbind-specs)
    • 放宽结构化绑定定制点查找规则(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#fix-structbind-cp)
    • 允许结构化绑定访问成员(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#fix-structbind-access)
  • 基于范围的 for 循环(Range-based for loop)(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#range-based-for)
    • 基于范围的 for 循环的 init 语句(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#init-range-for)
    • 放宽基于范围的 for 循环定制点查找规则(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#fix-range-for-cp)
  • 属性(Attributes)(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#attributes)
  • 字符编码(Character encoding)(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#encoding)
    • char8_t(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#char8t)
    • 更强的 Unicode 要求(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#stronger-unicode)
  • 语法糖(Sugar)(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#sugar)
    • 指定初始化器(Designated initializers)(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#designated-init)
    • 位域的默认成员初始化器(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#bitfield-def-init)
    • 更多可选的 typename(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#less-typename)
    • 嵌套 inline 命名空间(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#nested-inline-ns)
    • using enum(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#using-enum)
    • new 表达式中的数组大小推导(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#fix-arr-size)
    • 别名模板的类模板参数推导(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#ctad-alias)
    • constinit(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#constinit)
    • 有符号整数为二的补码(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#int-twos-compl)
    • 可变参数宏的 __VA_OPT__(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#va-opt)
    • 具有不同异常规范的显式默认函数(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#diff-except-spec)
    • 销毁 operator delete(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#destr-delete)
    • 条件 explicit 构造函数(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#explicit-conditional)
    • 特性测试宏(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#feature-test-macros)
    • 已知长度到未知长度数组的转换(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#array-conv)
    • 更多局部对象和右值引用的隐式移动(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#more-impl-moves)
    • T*bool 的转换是窄化转换(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#narrowing-ptr-bool-conv)
    • 弃用 volatile 的某些用法(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#depr-volatile)
    • 弃用下标中的逗号运算符(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#depr-comma-subs)
  • 修复(Fixes)(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#fixes)
    • 类模板参数推导中的初始化列表构造函数(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#fix-init-list-ctad)
    • const& 限定的成员指针(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#fix-const-qual)
    • 简化隐式 lambda 捕获(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#fix-impl-capture)
    • 默认拷贝构造函数中的 const 不匹配(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#fix-const-mismatch)
    • 特化上的访问检查(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#fix-spec-access-check)
    • ADL 和不可见的函数模板(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#fix-adl)
    • 指定常量求值何时需要 constexpr 函数定义(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#fix-constexpr-inst)
    • 为低级对象操作隐式创建对象(https://oleksandrkvl.github.io/2021/04/02/cpp-20-overview.html#fix-impl-creation)

概念(Concepts)

概念的基本思想是指定模板参数需要满足什么条件,以便编译器在实例化之前进行检查。结果是,如果有错误消息,会更加清晰,比如“未满足约束 X”。在 C++20 之前,可以使用复杂的 enable_if 构造,或者在模板实例化时失败并出现难以理解的错误消息。使用概念后,失败会提前发生,错误消息也更清晰。

Requires 表达式

让我们从 requires 表达式开始。它是一个包含对模板参数实际要求的表达式,如果满足则求值为 true,否则为 false

template<typename T>
concept Example = requires(T x) // 可选的一组虚构参数
{
    // 简单要求:表达式必须合法
    x++; // 表达式必须合法

    // 类型要求:`typename T`,T 类型必须是合法类型
    typename T::value_type;
    typename S;

    // 复合要求:{表达式}[noexcept][-> 概念];
    // {表达式} -> 概念 等价于
    // 要求 概念<...>;
    {*x}; // 解引用必须合法
    {*x} noexcept; // 解引用必须是 noexcept

    // 解引用必须返回 T::value_type
    {*x} noexcept -> std::same_as<T::value_type>;

    // 嵌套要求:要求 概念名<...>;
    requires Addable<T>; // 约束 Addable 必须被满足
};

概念(Concept)

概念就是一组这样的约束或其逻辑组合的命名集合。概念和 requires 表达式都求值为编译期 bool 值,并且可以像普通值一样使用,例如在 if constexpr 中。

template<typename T>
concept Addable = requires(T a, T b)
{
    a + b;
};

template<typename T>
concept Dividable = requires(T a, T b)
{
    a/b;
};

template<typename T>
concept DivAddable = Addable<T> && Dividable<T>;

template<typename T>
void f(T x)
{
    if constexpr(Addable<T>)
    {
        /*...*/
    }
    else if constexpr(requires(T a, T b) { a + b; })
    {
        /*...*/
    }
}

Requires 子句

要实际约束某些东西,我们需要 requires 子句。它可以出现在 template<> 块之后,或者作为函数声明的最后一个元素,甚至两个地方同时出现,lambda 也包括在内:

template<Addable T>
auto f1(T a, T b) requires Subtractable<T>; // Addable && Subtractable

auto l = []<Addable T>(T a, T b) requires Subtractable<T>{};

template<Addable T>
class C;

// 臭名昭著的 `requires requires`。第一个 requires 是 requires 子句,
// 第二个是 requires 表达式。如果不想引入新的概念,这很有用。
template<typename T>
    requires requires(T a, T b) {a + b;}
auto f4(T x);

更清晰的方式是在模板参数列表中使用概念名称代替 class/typename 关键字:

template<Addable T>
void f();

template<Addable... Ts>
void f();

模板模板参数也可以被约束。在这种情况下,参数必须比参数更少或相等约束。无约束的模板模板参数仍然可以接受有约束的模板作为参数:

template<typename T>
concept Integral = std::integral<T>;

template<typename T>
concept Integral4 = std::integral<T> && sizeof(T) == 4;

// requires 子句在这里也有效
template<requires Integral<typename T> typename T>
void f2(){}

// f() 和 f2() 形式等价
template<Integral T>
void f(){ f2<T>(); }

// 无约束的模板模板参数可以接受有约束的参数
template<template<typename> typename T>
void f3(){}

template<Integral T>
struct S1{};

template<Integral4 T>
struct S2{};

template<Addable T>
struct S3{};

void test()
{
    f<S1>();       // OK
    f<S2>();       // OK
    // 错误,S3 受 Integral4 约束,而 Integral4 比
    // f() 的 Integral 更约束
    f<S3>();

    // 全部 OK
    f3<S1>();
    f3<S2>();
    f3<S3>();
}

具有不满足约束的函数会变得“不可见”:

template<typename T>
struct X
{
    void f() requires std::integral<T> {}
};

void f()
{
    X<double> x;
    x.f();     // 错误
    auto pf = &X<double>::f; // 错误
}

约束 auto

现在普通函数允许使用 auto 参数,使其像泛型 lambda 一样通用。概念可用于在各种上下文中约束占位符类型(auto/decltype(auto))。对于参数包,MyConcept... Ts 要求 MyConcept 对包中的每个元素为真,而不是对整个包,例如 requires<C1> && requires<C2> && ... && requires<Cn>

template<typename T>
concept is_sortable = true;

auto l = [](auto x){};

void f1(auto x){}                          // 无约束模板
void f2(is_sortable auto x){}             // 有约束模板

template<is_sortable T>
auto f3(is_sortable auto x, auto y)
{
    // 注意约束名称和 `auto` 之间不允许有任何东西
    is_sortable auto z = 0;
    return 0;
}

template<is_sortable... TypePack>
void f4(TypePack... args){}

// 接受两个参数
int f(is_sortable auto, C auto);

template<typename T>
concept C = true;

// 绑定第二个参数
C auto v = f(1, 2); // 意思是 C<decltype(2)>

struct X
{
    operator is_sortable auto() { return 0; }
};

auto f5() -> is_sortable decltype(auto)
{
    f4<1,2,3>(1,2,3);
    return new is_sortable auto(1);
}

按约束的部分排序

本节灵感来源于文章按约束排序,作者 Andrzej Krzemieński。如需更深入的解释,请查阅该文章。

除了为单个声明指定要求外,约束还可用于为普通函数、模板函数或类模板选择最佳候选。为此,约束具有部分排序的概念,即一个约束可以比另一个约束“至少”或“更多”约束,或者它们可以“无序”(无关)。编译器将约束分解(标准使用术语“规范化”,但我认为“分解”听起来更好)为原子约束的合取/析取。直观地说,C1 && C2C1 更约束,C1C1 || C2 更约束,任何约束都比无约束的声明更约束。当存在多个满足约束的候选时,选择最受约束的那个。如果约束无序,则用法不明确。

template<typename T>
concept integral_or_floating = std::integral<T> || std::floating_point<T>;

template<typename T>
concept integral_and_char = std::integral<T> && std::same_as<T, char>;

void f(std::integral auto)                      {} // #1
void f(integral_or_floating auto)               {} // #2
void f(std::same_as<char> auto)                 {} // #3

// 调用 #1,因为 std::integral 比 integral_or_floating(#2) 更约束
f(int{});
// 调用 #2,因为它是唯一满足约束的
f(double{});
// 错误,#1、#2 和 #3 的约束都满足,但无序
// 因为 std::same_as<char> 只出现在 #3 中
f(char{});

void f(integral_and_char auto)                  {} // #4

// 调用 #4,因为 integral_and_char 比 std::same_as<t>(#3) 和 std::integral(#1) 更约束
f(char{});

理解编译器如何分解约束以及何时能看到它们具有共同的原子约束并推导出它们之间的顺序非常重要。在分解过程中,概念名称会被替换为其定义

相似文章

C++ 标准库在过去十五年间一直在自我撤步,证据公开

Lobsters Hottest

一份详细的目录,列出了从 C++11 到 C++26 期间被正式弃用、非正式不推荐或由于 ABI 约束实际上已损坏但无法修复的 C++ 标准库特性。文章指出,C++ 委员会推出一系列替代品来替换其自身特性的模式始终如一,其中包含一个基准测试,显示 Rust 和 C++ 标准库容器之间的 P99 延迟差异高达 58 倍。

C++26 中 std::format 的改进

Lobsters Hottest

C++26 标准对 std::format 库进行了多项改进,包括直接指针格式化、路径格式化、constexpr 支持,以及为 std::println 新增的空行重载。

C++26:标准库强化

Lobsters Hottest

C++26 引入了标准化的库强化机制,用于在运行时捕获常见的未定义行为(如越界访问)。基于 Google 的生产经验,此举仅带来 0.30% 的性能开销,同时将段错误减少了 30%。

信任你的编译器:现代C++

Hacker News Top

本文对比了旧的C++性能技巧与现代编译器的能力,表明编译器现在能够将朴素代码优化得比手工调整的技巧更好。包含在AMD Zen 5上使用Clang 21的基准测试。