C++11:常量表达式

constexpr

  C++ 本身已经具备了常数表达式的概念,比如 1+2, 3*4 这种表达式总是会产生相同的结果并且没有任何副作用。如果编译器能够在编译时就把这些表达式直接优化并植入到程序运行时,将能增加程序的性能。一个非常显著的例子就是在数组的定义阶段:

#define LEN 10

int len_foo() {
    return 5;
}

int main() {
    char arr_1[10];
    char arr_2[LEN];
    int len = 5;
    char arr_3[len+5];          // 非法,因为编译器需要知道为这个数组开辟多大的空间,如果是变量,就是非法的
    const int len_2 = 10;
    char arr_4[len_2+5];        // 合法
    char arr_5[len_foo()+5];  // 非法

    return 0;
}

  在 C++11 之前,可以在常量表达式中使用的变量必须被声明为 const,在上面代码中,len_2 被定义成了常量,因此 len_2+5 是一个常量表达式,所以能够合法的分配一个数组;

  而对于 arr_5 来说,C++98 之前的编译器无法得知 len_foo() 在运行期实际上是返回一个常数,这也就导致了非法的产生。

  C++11 提供了 constexpr 让用户显式的声明函数或对象构造函数在编译器会成为常数,这个关键字明确的告诉编译器应该去验证 len_foo 在编译时就应该是一个常数。

constexpr int len_foo() {
    return 3;
}

int main() {
    int arr[ len_foo() ] = {0};
    enum { e1 = len_foo(), e2 };

    constexpr int num = GetConst();

    return 0;
}

constexpr函数的限制

  1. 函数中只能有一个return语句(有极少特例)
  2. 函数必须返回值(不能是void函数)
  3. 在使用前必须已有定义
  4. return返回语句表达式中不能使用非常量表达式的函数、全局数据,且必须是一个常量表达式
#include<iostream>
using namespace std;

//err,函数中只能有一个return语句
constexpr int data() {
	constexpr int i = 1;
	return 10;
}

constexpr int data2() {
	//一个constexpr函数,只允许包含一行可执行代码
	//但允许包含typedef、 using 指令、静态断言等。
	static_assert(1, "fail");
	return 100;
}

int a = 3;
constexpr int data3() {
	return a;//err, return返回语句表达式中不能使用非常量表达式的函数、全局数据
}

int main() {
	constexpr int func(); //函数声明,定义放在main函数后面
	constexpr int c = func();  //err, 无法通过编译, 在使用前必须已有定义

	return 0;
}

constexpr int func() {
	return 1;
}

常量表达式的构造函数有以下限制:

  • 函数体必须为空
  • 初始化列表只能由常量表达式来赋值
struct Date {
public:
	int year;
	int month;
	int day;
public:
	constexpr Date(int y, int m, int d) : year(y), month(m), day(y) {}
	constexpr int GetYear() { return year; }
	constexpr int GetMonth() { return month; }
	constexpr int GetDay() { return day; }
};

int main() {
	constexpr Date PRCfound {1949, 10, 1};
	constexpr int foundmonth = PRCfound.GetMonth();

	cout << foundmonth << endl;  // 10

	return 0;
}

此外,constexpr的函数可以使用递归

constexpr int fibonacci(const int n) {
    return n == 1 || n == 2 ? 1 : fibonacci(n-1)+fibonacci(n-2);
}