第7.2章: constexpr 对象生成
概述
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 函数,编译器就必须在编译期间执行
该函数。
如果该计算无法在编译期完成,编译就会失败。这种情况发生在:
- 被调用的函数不是
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)
}
所有值都已预先计算好,并直接存储在程序映像中。
要点
digits10告诉我们该类型能容纳多少个 10 的幂。constexpr函数可以用循环生成数组。- 返回
auto让编译器推导数组类型。 inline constexpr变量强制编译期求值。- 如果计算无法在编译期完成,编译就会失败。
- 生成的表直接嵌入程序映像中。