constexpr
1. 常量表达式
是指值不会改变,并且在编译过程就能得到计算结果的表达式。这是很大的优化:假如有些事情可以在编译时做,它将只做一次,而不是每次程序运行时都计算。
1)字面值常量是常量表达式,如123,'a',3.14等。
2)跟字面值常量相关的一些表达式也是常量表达式,如123+3.14,2<<2等。
3)在C++(C中不是)中用常量表达式初始化的const对象也是常量表达式,如int const a = 5;语句中a就是常量表达式。
int main() { const int a1 = 10; // a1是常量表达式 const int a2 = a1 + 20; // a2是常量表达式 int a3 = 5; // a3不是常量表达式 const int a4 = a3; // a4不是常量表达式,因为a4的值程序运行时才知道 return 0; }
2. constexpr进行修饰
除了1中介绍的3中常量表达式,那程序员能不能指定一个式子让编译器在编译期间就把它给算出来呢? 当然是可以的。
C++11引入了constexpr关键字,constexpr可以用来修饰变量、函数、构造函数。一旦以上任何元素被constexpr修饰,那么等于说是告诉编译器:
我想把这些式子看成常量表达式,请在编译期间就直接计算它们的值,然后完成全部的替换。
1)constexpr修饰函数
a. 函数体只有单一的return语句。在C++14中这条己经不再是限制。
b. 函数体必须有返回值(C++11中要求不能是void函数,但C++14己不再限制)。
c. constexpr函数内部不能调用非常量表达式的函数,会造成编译失败。
d. return返回语句表达式中不能使用非常量表达式的函数、全局数据,且必须是一个常量表达式。
e. constexpr函数在调用时若传入的实参均为编译期己知的,则返回编译期常量。只要任何一个实参在编译期未知,则它的运作与普通函数无异,将产生运行期结果。
f. constexpr函数的构成条件不满足时,就会变成一个普通的函数。
constexpr int func() { return 10; } constexpr int func1(int x) // 根据传递进来的参数,既可当编译期常量表达式函数,也可以当作普通函数使用 { return x + 2; } int main() { int arr[func()]; int x = 0; //非constexpr变量 const int y = 0; constexpr int a = 2; // a为编译期常量 constexpr int b = a + 4; // b为编译期常量 constexpr int c = y + 1; // c为编译期常量,y直接取自符号表,而不是来自内存值 constexpr auto N = 10; constexpr int d = func1(N); // 实参为constexpr constexpr int e = func1(x); // 实参非constexpr, func1就是普通函数,但e却是constexpr,所以编译失败 return 0; }
2)类的constexpr对象
a. 自定义类的构造函数须为constexpr函数。
b. constexpr不能用于修饰virtual函数。因为virtual是运行时的行为,与constexpr的意义冲突。
c. C++11中被声明为constexpr成员函数会自动设为const函数,但C++14中不再自动设为const函数。
d. 用constexpr构造函数创建的对象也可以当作常量使容用。
class Point { double x, y; public: // 常量表达式构造函数 constexpr Point(double x = 0, double y = 0) noexcept : x(x),y(y) {} // 成员函数(constexpr不允许用于修饰virtual函数) constexpr double getX() const noexcept { return x; } constexpr double getY() const noexcept { return y; } // 以下两个函数在C++11中无法声明为constexpr原因如下: // 1.C++11中被声明为constexpr成员函数会自动设为const函数,而这两个函数需要更改成员变量的值,而const成员函数 // 却不允许需改成员的值,会产生编译错误。C++14中constexpr函数不再默认为const函数,因此可以修改成员变量。 // 2.C++11中constexpr函数不能返回void型,但C++14中不再限制。 constexpr void setX(double newX) noexcept { x = newX; } constexpr void setY(double newY) noexcept { y = newY; } }; constexpr Point midpoint(const Point& p1, const Point& p2) noexcept { //p1和p2均为const对象,会调用const版本的getX()和getY() return { (p1.getX() + p2.getX()) / 2, (p1.getY() + p2.getY()) / 2 }; } int main() { constexpr Point p1(9.4, 27.7); // ok, p1为编译期常量 constexpr Point p2(28.8, 5.3); // ok,同上 constexpr auto mid = midpoint(p1, p2); // mid为编译期常量,mid.getX()也是编译期常量 return 0; }