Overview
1.Constant expressions 常量表达式的性能优化空间
2.Compile-time constants v.s. Runtime constants 编译期常量 v.s. 运行时的常量
3.The constexpr keyword constexpr 关键字
Constant expressions 常量表达式的性能优化空间
1.我们先回顾一个最基础的常量表达式, 比如 x = 3 + 4; 这个常量表达式有什么值得关注的呢 ? cpp 关注点是永远关注性能, 哪怕是最不起眼的表达式, 这里面其实隐藏了一个可能的性能优化点
2.代码在编译的时候, 编译器会生成计算 3+4 的可执行的逻辑; 程序在执行的时候, 3+4 这个运算会执行一次 (也就是之前生成的可执行的逻辑这时候执行一次); 如果我们这个程序要执行1亿次, 那么 3+4 将会被执行1亿次 (也就是1次 * 1B), 然后x=7这个赋值执行1亿次; 其实我们想想这个计算的过程是永远不会变的, 算 1 次其实就够了;
#include<iostream>
int main() {
int x = 3 + 4;
std::cout << x << '\n';
return 0;
}
A constant expression is an expression that can be evaluated by the compiler at compile-time. To be a constant expression, all the values in the expression must be known at compile-time (and all of the operators and functions called must support compile-time evaluation).
When the compiler encounters a constant expression, it may evaluate the expression at compile-time, and then replace the constant expression with the result of the evaluation.
Evaluating constant expressions at compile-time makes our compilation take longer (because the compiler has to do more work), but such expressions only need to be evaluated once (rather than every time the program is run). The resulting executables are faster and use less memory.
intuitively,
1.常量表达式在编译时就可以做完计算的表达式, 注意这里不是在执行期执行计算逻辑, 而是编译器对一部分逻辑放在编译期就算完了;
2.在编译器算完之后, 就会产出一个结果比如上述例子算出来7, 然后再执行期用算出来的结果来替换常量表达式, 也就是将3+4这个直接换成7; 在这个case上, 我们每一次执行都不需要执行任何的+的运算, 因为在编译期间都算完了, 相当于读取一个常数;
3.这种常量表达式的存在, 使得我们的编译事件会更长, 这很容易理解, 是因为编译期多干了一些事情; 但从整体来看计算1次总比计算1B次要大量省时间, 生成的可执行文件速度更快并且内存使用更少;
Compile-time constants v.s. Runtime constants 编译期常量 v.s. 运行时的常量
A Compile-time constant is a constant whose value is known at compile-time. Literals (e.g. ‘1’, ‘2.3’, and “Hello, world!”) are one type of compile-time constant.
what about const variables? Const variables may or may not be compile-time constants.
Runtime constants are constants whose initialization values aren’t known until runtime.
intuitively,
1.编译期常量是在编译期内固定下来的常量; 其实, 我们想我们熟悉的一些字面量, 比如 1, 2, 3, 2.4, “hello”, 这些都是在编译期常量
2.这里我们再思考一种特殊的变量, 想一下 const 的变量是编译期常量吗? 直觉上是的, 因为 const 的值是不变的东西, 这不是很适合在编译器直接搞好吗? 事实上, const 变量可能是编译期常量, 也可能不是编译期常量
3.如果 const 变量在初始化的时候是一个 constant 表达式, 那么 const 变量就是一个编译期常量
#include <iostream>
int main() {
const int x = 3; // x 是一个编译期常量
const int y = 4; // y 是一个编译期常量
const int z = x + y; // x + y 是一个constant表达式, 所以z也是一个编译期常量
const double gravity{9.8}; // gravity是编译期常量
std::cout << z << '\n';
return 0;
}
intuitively,
1.和编译器常量不同, 运行时的常量就是初始化这个常量的时候, 这个常量还是未知的, 非要直到运行时才能确定下来该常量的值; 什么时候初始化过程中还未知呢? 一个典型例子就是调用函数 cin >> y; 这种
#include <iostream>
int getNumber() {
std::cout << "Enter a number: ";
int y{};
std::cin >> y;
return y;
}
int main() {
const int x{3}; // x是一个编译期的常量
const int y{getNumber()}; // y是一个运行期的常量, 因为在初始化的时候, getNumber()的返回值还是未知的
const int z{x + y}; // x+y是一个运行期的常量, z也是一个运行期常量, x+y 虽然看起来挺确定的, 但是在这种未知的语境下还是未知的, 所以还是运行期的常量;
return 0;
}
The constexpr keyword constexpr 关键字
When you declare a const variable, the compiler will implicitly keep track of whether it’s a runtime or compile-time constant. In most cases, this doesn’t matter for anything other than optimization purposes, but there are a few cases where C++ requires a constant expression (we’ll cover these cases later as we introduce those topics). And only compile-time constant variables can be used in a constant expression.
Because compile-time constants also allow for better optimization (and have little downside), we typically want to use compile-time constants wherever possible.
When using const, our variables could end up as either a compile-time const or a runtime const, depending on whether the initializer is a compile-time expression or not. In some cases, it can be hard to tell whether a const variable is a compile-time const (and usable in a constant expression) or a runtime const (and not usable in a constant expression).
we can enlist the compiler’s help to ensure we get a compile-time const where we desire one. To do so, we use the constexpr keyword instead of const in a variable’s declaration. A constexpr (which is short for “constant expression”) variable can only be a compile-time constant. If the initialization value of a constexpr variable is not a constant expression, the compiler will error.
intuitively,
1.声明一个常量的时候, 编译器会隐式地决定是一个运行期的还是编译器的常量; 在大多数情况下, 我们也不用关心是哪种常量
2.但这里我们核心的研究对象其实是常量表达式, 更具体来说, 我们关心的内容是常量表达式里面的常量是编译器常量还是运行期常量; 在实际开发中用常量表达式的情形下, 常量表达式里面只能用编译期的常量, 而不能用运行期的常量
3.因为之前 x = 3+4 的例子告诉我们, 编译期的常量还有一定的优化空间, 所以我们通常会为了性能尽可能地使用编译器常量
4.虽然我们想多利用编译期的常量, 但有时候很难去辨别一个 const varaible 是编译器常量还是运行期常量; 如果是一个编译期的常量, 那就能将这个常量用在常量表达式里面, 反之如果是一个运行期的常量, 我们就不能用在常量表达式中 (会报错) ; 比如下面的例子中, w的定义取决于 getValue() 函数的定义
int x{5}; // not const at all
const int y{x}; // obviously a runtime const (since initializer is non-const)
const int z{5}; // obviously a compile-time const (since initializer is a constant expression)
const int w{getValue()}; // not obvious whether this is a runtime or compile-time const
5.我们期望让编译器去确保我们能得到 “确定性” 的 [编译时常量], 为了达到这个目的, 我们采用的是 constexpr 关键字修饰, 而不是用 const 关键字修饰; 用 constexpr 修饰的 variable 只能是编译期的常量, 如果用 constexpr 修饰的 variable 不是一个常量表达式, 那么编译器会报错
#include <iostream>
int five() {
return 5;
}
int main() {
constexpr double gravity{9.8};
constexpr int sum{4+5};
constexpr int something{sum};
std::cout << "enter your age:";
int age{};
std::cin >> age;
constexpr int myAge{age}; // 报错: constexpr variable 'myAge' must be initialized by a constant expression
constexpr int f{five()}; // 报错: constexpr variable 'f' must be initialized by a constant expression
return 0;
}
constexpr v.s. const
这里我们对比下constexpr 和 const关键词怎么使用
Any variable that should not be modifiable after initialization and whose initializer is known at compile-time should be declared as constexpr.
Any variable that should not be modifiable after initialization and whose initializer is not known at compile-time should be declared as const.
intuitively,
1.首先这两个都是要满足定义初始化后不能修改的需求, 都是 const
2.在初始化常量时, 二者初始化的要求不同, constexpr 初始化在编译期已知, 而 const 初始化在编译期时未知
3.其实说白了就是: 当我们要一个常量属性的 x = 某个表达式, 这里”某个表达式”如果在写代码的时候, 代码逻辑都是唯一确定的, 那就用 constexpr 声明, 如果代码逻辑中有任何一点确定不下来的地方 (比方说调用了函数或者 cin>> 输入这种) , 那么只能声明称 const
Reference
1.https://www.learncpp.com/cpp-tutorial/compile-time-constants-constant-expressions-and-constexpr/
转载请注明来源, from goldandrabbit.github.io