第7.2章: constexpr 对象生成

English

概述

constexpr 函数可以在编译期构建对象。在本章中,我们在一个 constexpr 函数内部用循环生成 10 的幂查找表,返回该表, 并强制编译器在编译期间把它算出来。

这种技术适用于预计算表,例如 10 的幂、数字转换表, 以及其他不应在运行时计算的固定数据。

1. 确定能容纳多少个 10 的幂

我们使用 std::uint_least64_t 来避免溢出。安全的 10 的幂的个数由下式给出:


constexpr std::size_t pow10_count =
    ::std::numeric_limits<::std::uint_least64_t>::digits10 + 1;

对 64 位整数而言,这通常是 19(即 10⁰ 到 10¹⁸)。

2. 用 constexpr 函数生成表

constexpr 函数可以包含循环。我们用它把 10 的幂填入 ::fast_io::array。返回类型是 auto,因此编译器会推导出数组类型。


constexpr auto make_pow10()
{
    ::fast_io::array<::std::uint_least64_t, pow10_count> arr{};
    ::std::uint_least64_t value{1};

    for(std::size_t i{}; i != pow10_count; ++i)
    {
        arr[i] = value;
        value *= 10;
    }

    return arr;
}

这个函数在编译期构建整张表。

3. 强制编译期求值

要强制编译器在编译期间计算该表,请定义一个 inline constexpr 全局变量:


inline constexpr auto pow10_table = make_pow10();

constexpr 变量会强制其初始化器在编译期求值。如果初始化器 调用了一个 constexpr 函数,编译器就必须在编译期间执行 该函数。

如果该计算无法在编译期完成,编译就会失败。这种情况发生在:

由于该变量是 inline 的,它可以放在头文件中 而不会造成重复符号错误。

4. 在 main() 中使用该表


int main()
{
    using namespace ::fast_io::iomnp;
    println(pow10_table[0]);  // 1
    println(pow10_table[1]);  // 10
    println(pow10_table[5]);  // 100000
    println(pow10_table[18]); // 10^18 (on 64-bit)
}

所有值都已预先计算好,并直接存储在程序映像中。

要点