C++23 对部分特性的 constexpr 支持

文章目录

    • [1. `std::bitset (P2417R2)`](#1. std::bitset (P2417R2))
      • [1.1 概述](#1.1 概述)
      • [1.2 具体变化](#1.2 具体变化)
      • [1.3 优势](#1.3 优势)
    • [2. `std::unique_ptr (P2273R3)`](#2. std::unique_ptr (P2273R3))
      • [2.1 概述](#2.1 概述)
      • [2.2 具体变化](#2.2 具体变化)
      • [2.3 优势](#2.3 优势)
    • [3. `std::type_info::operator== (P1328R1)`](#3. std::type_info::operator== (P1328R1))
      • [3.1 概述](#3.1 概述)
      • [3.2 具体变化](#3.2 具体变化)
      • [3.3 优势](#3.3 优势)
    • [4. 一些 `<cmath>` 函数 (P0533R9)](#4. 一些 <cmath> 函数 (P0533R9))
      • [4.1 概述](#4.1 概述)
      • [4.2 具体变化](#4.2 具体变化)
      • [4.3 优势](#4.3 优势)
    • [5. `std::to_chars` 和 `std::from_chars` 的整型重载 (P2291R3)](#5. std::to_charsstd::from_chars 的整型重载 (P2291R3))
      • [5.1 概述](#5.1 概述)
      • [5.2 具体变化](#5.2 具体变化)
      • [5.3 优势](#5.3 优势)

在 C++ 编程中, constexpr 关键字自 C++11 引入以来,便致力于让更多的计算在编译时完成,以此提升程序的性能与效率。C++23 进一步拓展了 constexpr 的应用范畴,为 std::bitsetstd::unique_ptrstd::type_info::operator==、部分 <cmath> 函数以及 std::to_charsstd::from_chars 的整型重载等带来了 constexpr 支持。下面将详细介绍这些特性在 C++23 中的 constexpr 支持情况。

1. std::bitset (P2417R2)

1.1 概述

std::bitset 是一个固定大小的位序列容器,在 C++23 之前,仅有一个构造函数和 operator[] 被标记为 constexpr。P2417R2 提案对 std::bitsetconstexpr 接口进行了扩展,鉴于 std::string 能够是 constexprstd::bitset 的所有内部结构以及完整的 API 如今都能够是 constexpr

1.2 具体变化

在 C++23 里,std::bitset 的更多构造函数和成员函数被标记为 constexpr。例如:

cpp 复制代码
#include <bitset>
#include <iostream>

int main() {
    constexpr short i = 15;
    constexpr int numberOfBitsInInt = sizeof(i) * 8;
    // 使用 constexpr 构造 std::bitset
    constexpr std::bitset<numberOfBitsInInt> bits(i); 
    std::cout << "i:" << i << ", i as binary: " << bits << '\n'; 
    return 0;
}

在上述代码中,std::bitset 的构造函数被标记为 constexpr,从而能够在编译时构建 bitset 对象。

1.3 优势

这一改进让开发者能够在编译时对 bitset 进行操作和计算,减少运行时的开销。例如,在编译时就能确定位序列的值,进而避免在运行时进行额外的计算。

2. std::unique_ptr (P2273R3)

2.1 概述

std::unique_ptr 是一种智能指针,用于管理动态分配的对象,保证对象的所有权唯一。P2273R3 提案被接纳后,std::unique_ptr 在 C++23 中支持 constexpr

2.2 具体变化

多个构造函数、析构函数、赋值运算符等都被标记为 constexpr。例如:

cpp 复制代码
#include <memory>

struct Car {
    virtual ~Car() = default;
    constexpr virtual int speed() const = 0;
};

struct Mercedes : Car {
    constexpr int speed() const override { return 5; }
}; 

struct Toyota : Car {
    constexpr int speed() const override { return 6; }
}; 

struct Tesla : Car {
    constexpr int speed() const override { return 9; }
}; 

constexpr Car* CreateCar(int i) {
    switch(i) {
        case 0: return new Mercedes{};
        case 1: return new Toyota{};
        case 2: return new Tesla{};
    }
    return nullptr; 
}

constexpr int FastestCar() {
    int max = -1;
    int maxId = -1;
    for(int i = 0; i < 3; ++i) {
        const auto* car = CreateCar(i);
        if(car->speed() > max) {
            max = car->speed();
            maxId = i;
        }
        delete car;
    }
    return maxId; 
}

void Use() {
    static_assert(FastestCar() == 2); 
}

在这个示例中,CreateCarFastestCar 函数能够在编译时运行,并且 std::unique_ptr 的相关操作也能够在编译时完成。

2.3 优势

这使得 std::unique_ptr 可以在编译时进行实例化和操作,有助于在编译时进行更多的检查和优化,减少运行时的错误。

3. std::type_info::operator== (P1328R1)

3.1 概述

std::type_info 用于在运行时获取类型信息,而 std::type_info::operator== 用于比较两个类型信息对象是否指代相同的类型。在 C++23 之前,typeid 虽允许在常量表达式中使用,但得到的 std::type_info 对象因没有 constexpr 成员函数而无法使用。P1328R1 提案建议将 std::type_info::operator== 标记为 constexpr

3.2 具体变化

在 C++23 中,std::type_info::operator== 被正式标记为 constexpr。例如:

cpp 复制代码
#include <iostream>
#include <typeinfo>

class Base { virtual void foo() {} };
class Derived : public Base {};

int main() {
    constexpr bool sameType = (typeid(Base) == typeid(Base));
    std::cout << "Same type: " << (sameType ? "Yes" : "No") << std::endl;
    return 0;
}

在上述代码中,typeidoperator== 的比较操作能够在编译时完成。

3.3 优势

这使得在常量表达式中使用 typeid 更具实用性,能够在编译时进行类型比较,提前发现类型不匹配的问题。

4. 一些 <cmath> 函数 (P0533R9)

4.1 概述

<cmath> 头文件包含了众多数学函数。有提案被接受作为 C++23 的一部分,该提案使大量 <cmath> 函数支持 constexpr,不过在 clang/llvm 中尚未实现。

4.2 具体变化

std::fmax 为例,可通过以下策略使其支持 constexpr

cpp 复制代码
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_CXX23 float fmax(float __x, float __y) _NOEXCEPT {
  if consteval { 
    return __builtin_fmax(__x, __y); 
  } else {
    return ::fmaxf(__x, __y); 
  }
}

在编译时,如果是常量求值,就使用 constexpr 内置函数;否则,使用实际的实现。

4.3 优势

让数学函数能够在编译时进行计算,减少运行时的计算开销,提高程序的性能。

5. std::to_charsstd::from_chars 的整型重载 (P2291R3)

5.1 概述

std::to_charsstd::from_chars 是 C++17 引入的用于字符序列和数值之间转换的函数。P2291R3 提案使它们的整型重载支持 constexpr

5.2 具体变化

在 C++23 中,这些整型重载函数可以在编译时进行转换操作。例如:

cpp 复制代码
#include <charconv>
#include <iostream>

int main() {
    char buffer[10];
    constexpr int value = 42;
    auto result = std::to_chars(buffer, buffer + sizeof(buffer), value);
    if (result.ec == std::errc()) {
        std::cout << "Converted value: " << std::string(buffer, result.ptr) << std::endl;
    }
    return 0;
}

在上述代码中,std::to_chars 的整型重载可以在编译时将整数转换为字符序列。

5.3 优势

在编译时进行字符和数值的转换,能够避免运行时的开销,并且可以在编译时检查转换是否成功,提高程序的安全性。

综上所述,C++23 对这些特性的 constexpr 支持进一步增强了编译时计算的能力,减少了运行时的开销,提高了程序的性能和安全性。开发者可以更充分地利用编译时的计算资源,编写更高效、更安全的代码。

相关推荐
Jerry4 小时前
LeetCode 28. 找出字符串中第一个匹配项的下标
算法
Jerry6 小时前
LeetCode 459. 重复的子字符串
算法
海石8 小时前
1500分的题目,确实有实力,不过还是我略胜一筹
算法·leetcode
海石8 小时前
【记忆化搜索】条条大路通AC,走好适合你的那一条,走到后再考虑走得快
算法·leetcode
Jerry10 小时前
LeetCode 151. 反转字符串中的单词
算法
a11177613 小时前
LM 算法迭代过程动画演示(SLAM)
算法
头茬韭菜13 小时前
Context 的生死抉择:四层压缩、截断算法与 Session Memory
算法·ai
Jerry13 小时前
LeetCode 541. 反转字符串 II
算法
Jerry13 小时前
LeetCode 344. 反转字符串
算法
搞科研的小刘选手14 小时前
【香港大学主办&IEEE出版】第六届计算机视觉、应用与算法国际学术会议(CVAA 2026)
算法·计算机视觉·应用·学术会议