-
constexpr
关键字的概念丰富,作用于对象和函数时意义不同。 -
作用于对象,
constexpr
描述的对象是常量,且值在编译期已知。
int sz; // 未初始化的int
constexpr auto arraySize1 = sz; // error! sz的值在编译期未知
const auto arraySize2 = sz; // ok. arraySize2是sz的const copy。MSVS过编译但链接报错,GCC可以运行且输出为0。
std::array<int, sz> data1; // error, std::array的size必须在编译期已知
std::array<int, arraySize2> data2; // error, 同上
constexpr auto arraySize3 = 10; // ok
std::array<int, arraySize3> data3; // ok
-
总结:所有
constexpr
对象都是const
,但不是所有const
对象都是constexpr
。当你需要保证一个变量是编译期已知(已初始化)的常量时,使用constexpr
而不是const
。 -
作用于函数,
constexpr
描述的函数被调用时如果参数是编译期常量,则其值也在编译期被计算;如果有一个或更多参数在编译期未知,则表现与普通函数一样。相当于是一个纯编译期的常量函数与一个普通函数的二合一。 -
接下来通过一个情景展示
constexpr
的使用如何扩展到超出你想象的范围。假设我们要在一个std::array
中存储某个系统的状态,状态的数量(数组空间)大小是 3 n 3^n 3n,其中 n 或许是或许不是一个常量。这里有两个原因使我们不能使用std::pow
函数:一,它作用于浮点型;二,它不是constexpr
,所以不能将它用于std::array
的size。那我们就自己写一个:
constexpr int pow(int base, int exp) noexcept {
return (exp == 0 ? 1 : base * pow(base, exp - 1));
}
...
constexpr auto num = 5;
std::array<int, pow(3, 5)> results;
- C++11对
constexpr
函数的限制是其只能有一个语句:return
,可以用条件运算符?:
代替 if-else 和用递归来代替循环。C++14放宽了这一要求,于是可以写一个循环版本的pow
函数:
constexpr int pow(int base, int exp) noexcept {
auto result = 1;
for (int i = 0; i < exp; ++i) result *= base;
return result;
}
constexpr
函数只能接受和返回字面类型(literal types),即在编译期有确定值的类型。所有内建类型除void
均满足这一点,而实际上用户定义的类型也可以满足,因为构造和其它成员函数可以被声明为constexpr
:
class Point {
public:
constexpr Point(double xVal = 0, double yVal = 0) noexcept
: x(xVal), y(yVal)
{}
constexpr double xValue() noexcept { return x; }
constexpr double yValue() noexcept { return y; }
constexpr void setX(double newX) noexcept { x = newX; } // C++14
constexpr void setY(double newY) noexcept { y = newY; }
private:
double x, y;
};
- C++11中,两个 setter 函数不能被声明为
constexpr
,因为(1)它们的返回类型是void
;(2)constexpr
隐含为const
条件,而它们更改了数据成员的值。C++14中放宽了这两个限制,于是它们也能是constexpr
函数了。于是,以下Point
的构建、运算等操作都可以在编译期完成:
constexpr
Point midPoint(const Point& p1, const Point& p2) noexcept
{
return Point((p1.xValue() + p2.xValue()) / 2,
(p1.yValue() + p2.yValue()) / 2);
}
constexpr
Point reflection(const Point& p) noexcept
{
Point result;
result.setX(-p.xValue());
result.setY(-p.yValue());
return result;
}
constexpr Point p1(9.4, 27.7);
constexpr Point p2(28.8, 5.3);
constexpr auto mid = midPoint(p1, p2);
constexpr auto reflectedMid = reflection(mid);
总结
constexpr
对象是const
,且其值在编译期已知(必须是经过初始化的)。constexpr
函数当使用编译期已知量作为参数调用时其结果也在编译期得出。constexpr
是对象或函数声明(接口)的一部分。