C++ 11 新特性: constexpr变量和constexpr函数

constexpr变量

字面值类型包括算术类型、引用、指针、枚举和数据成员都是字面值类型的聚合类。

聚合类的定义:

  • 所有成员都是public的。
  • 没有定义任何构造函数。
  • 没有类内初始值。
  • 没有基类,也没有虚函数。

如:

struct Data {
	int ival;
	string str;
};

如果一个类不是聚合类,但它符合下述要求,则它也是一个字面值常量类:

  • 数据成员必须是字面值类型
  • 类必须有一个constexpr构造函数
  • 如果一个数据成员含有类内初始值,则内置类型成员的初始值必须是一条常量表达式(其定义见下文);或者如果成员属于某种类类型,则初始值必须使用成员自己的constexpr构造函数
  • 类必须使用构造函数的默认定义

常量表达式是指值不会改变并且在编译过程就能得到计算结果的表达式。字面值属于常量表达式,用常量表达式初始化的const对象也是常量表达式。

const int max_files=20;			 	// max_files是常量表达式
const int limit=max_files+1;		//limit是常量表达式
int staff_size =27; 				// staff_size不是常量表达式
const int sz=get_size();			// sz不是常量表达式

尽管staff_size的初始始是个字面值常量,但由于它的数据类型非const,所以它不是常量表达式。尽管sz本身是一个常量,但它的具体值直到运行时才能得到,也不是常量表达式。

在一个复杂的系统中,很难分辨一个初始值到底是不是常量表达式。当然可以定义一个const变量并把它的初始值设为我们认为的某个常量表达式,但在实际使用中,尽管要求如此却常常发现初始值并非常量表达式的情况。

C++11规定,允许将变量声明为constexpr类型以便由编译器来验证变量的值是否是一个常量表达式。声明为constexpr的变量一定是一个常量,而且必须用常量表达式初始化。

constexpr int mf=20;		//20是常量表达式
constexper int limit=mf+1;	//mf+1是常量表达式
constxper int sz=size();	//只有当size是一个constxper函数时才是一条正确的声明语句

不能用普通函数作为constexpr变量的初始值,只能用constexpr函数去初始化constexpr变量。这种函数足够简单,以使得编译时就可以计算其结果。

一般来说,如果你认定变量是一个常量表达式,那就把它声明成constexpr类型。

constexpr函数

constexpr函数是指能用于常量表达式的函数。该函数要遵循几项约定:函数的返回类型及所有形参的类型都得是字面值类型,而且函数体中必须有且只有一条return语句:

constexpr int new_sz() { return 42; }
constexpr int foo = new_sz();

在对变量foo初始化时,编译器把对constexpr函数的调用替换成其结果值。为了能在编译过程中随时展开,constexpr函数被隐式地指定为内联函数。

constexpr函数体内也可以包含其它语句,只要这些语句在运行时不执行任何操作就行。例如,constexpr函数中可以有空语句、类型别名以及using声明。

需要注意的是,我们允许constexpr函数的返回值并非一个常量

//如果arg是常量表达式,则scale(arg)也是常量表达式
constexpr size_t scale(size_t cnt){ return new_sz() * cnt; }

当scale的实参是常量表达式时,它的表达式也是常量表达式,反之则不然:

int arr[scale(2)];	//正确:scale(2)是常量表达式
int i = 2;			//i不是常量表达式
int a2[scale(i)];	//错误:scale(i)不是常量表达式

当把scale函数用在需要常量表达式的上下文中时,如果其结果恰好不是常量表达式,编译器将发出错误信息。

注意,我们要把内联函数和constexpr函数定义在头文件中。因为内链函数是内部链接的,如果你在b.cpp中定义这个函数,那么在a.cpp中即使有这个函数声明,但由于内联函数是内部链接的,所以b.cpp不会提供其定义。所以在链接时a.obj无法找到这个函数的定义,便会出现无法解析的外部符号的错误

constexpr构造函数

我们知道,构造函数不能是const的。因为当我们创建一个类的const对象时,直到构造函数完成初始化过程,对象才能真正取得其”常量“属性。因此,构造函数在const对象的构造过程中可以可以向其写值。但是字面值常量类的构造函数可以是constexpr函数。事件上,一个字面值常量类必须至少提供一个constexpr函数。

constexpr构造函数可以声明成=default的形式(或者是删除函数的形式)。否则,constexpr构造函数体一般来说应该是空的,因为构造函数不能包含返回语句,而constexpr函数的唯一可执行语句就是返回语句。constexpr构造函数的声明形式如下:

class Debug {
public:
	constexpr Debug(int i) :i_(i) {}
private:
	int i_;
};

constexpr构造函数必须初始化所有数据成员,初始值或者使用constexpr构造函数,或者是一条常量表达式。

你可能感兴趣的:(C++)