引言:C++ 中常量的永恒之战
\n\n如果你曾经写过 C++(或者只是刚开始学习它),你肯定遇到过 const。这个神奇的前缀承诺变量的值不会改变。但随后 C++11 带来了 constexpr。混乱就此开始。
在 Stack Overflow 上,"constexpr 和 const 有什么区别?" 这个问题已经获得了数十万的浏览量。各种水平的程序员都会混淆这两个概念,将它们当作同义词使用。但这是一个大错误!
\n\n在本教程中,我们将一劳永逸地弄清楚 C++ 中 constexpr 和 const 的区别。你将了解到:
- \n
- 什么是编译时常量和运行时常量。 \n
- 为什么
const并不总是与性能有关。 \n constexpr如何将 C++ 转变为一种现代且安全的语言。 \n - 何时使用这些构造。 \n
让我们开始吧!🚀
\n\n第 1 章:const:一个承诺,而非保证
\n\n让我们从基础开始。C++ 中的 const 是一个类型限定符,它告诉编译器:“嘿,这个值在初始化后不能被改变。” 听起来很简单,但这里有一个陷阱。
示例 1:const 的使用
\n\n#include <iostream>\n\nint main() {\n const int x = 42; // 承诺:x 不会改变\n // x = 100; // 编译错误 — 很好!\n \n std::cout << x; // 将输出 42\n return 0;\n}\n\n一切看起来都不错。但是如果一个 const 变量的值是在运行时才计算出来的呢?
示例 2:在运行时计算的 const
\n\n#include <iostream>\n\nint get_value() {\n return 42; // 函数返回一个值\n}\n\nint main() {\n const int y = get_value(); // 仍然是 const 吗?\n // 但是 y 的值只有在运行时才知道!\n \n // 让我们尝试在数组中使用它:\n // int arr[y]; // 错误!y 不是编译时常量!\n \n return 0;\n}\n\n看,这就是主要陷阱!const 保护变量不被修改,但不能保证其值在编译时已知。如果值来自函数,它只在程序运行时才被计算出来。
关键要点: const 是关于不可变性的约定,而不是关于编译时可计算性的约定。
第 2 章:constexpr:存在于代码中的常量
\n\n这就是 constexpr 登场的时刻。这个说明符出现在 C++11 中,并从根本上改变了对常量的处理方法。constexpr 意思是:“这个值必须在编译时计算出来。” 或者,至少,它能够被计算出来。
示例 3:constexpr 变量
\n\n#include <iostream>\n\nint main() {\n constexpr int z = 42; // 保证:值在编译时已知\n \n int arr[z]; // 有效!z 是一个编译时常量\n \n std::cout << sizeof(arr) / sizeof(arr[0]); // 将输出 42\n return 0;\n}\n\n区别很明显:constexpr 允许在编译器需要知道确切值的地方使用变量——例如,用于数组大小或模板参数。
示例 4:constexpr 函数
\n\n但最有趣的部分是函数。constexpr 可以应用于函数,这样如果所有参数也都是编译时常量,它们就可以在编译时进行计算。
#include <iostream>\n\nconstexpr int square(int x) {\n return x * x;\n}\n\nint main() {\n constexpr int result = square(5); // 在编译时计算!\n int arr[result]; // 有效\n \n std::cout << result; // 25\n \n int runtime_value =