C++ 11 新标准规定,允许将变量声明为 constexpr 类型以便由编译器来验证变量的值是否是一个常量表达式。声明为 constexpr 的变量一定是一个常量,而且必须用常量表达式初始化:
constexpr int mf = 20; // 20 是常量表达式
constexpr int limit = mf + 1; // mf + 1 是常量表达式
constexpr int sz = size(); // 只有当 size 是一个 constexpr 函数时才是一条正确的声明语句
一般来说,如果你认定变量是一个常量表达式,那就把它声明成 constexpr 类型
尽管指针和引用都能定义成 constexpr,但它们的初始值却受到严格限制。一个 constexpr 指针的初始值必须是 nullptr 或者 0,或者是存储于某个固定地址中的对象。
函数体内定义的变量一般来说并非存放在固定地址中,因此 constexpr 指针不能指向这样的变量。相反地,定义于所有函数体之外的对象其地址固定不变,能用来初始化 constexpr 指针。
constexpr 函数
constexpr 函数是指能用于常量表达式的函数。定义 constexpr 函数的方法与其他函数类似,不过要遵循几项约定:函数的返回类型及所有形参的类型都得是字面值类型,而且函数体中必须有且只有一条 return 语句:
constexpr int new_sz() { return 42;}
constexpr int fsz = new_sz();
我们把 new_sz 定义成无参数的 constexpr 函数。因为编译器能在程序编译时验证 new_sz 函数返回的是常量表达式,所以可以用 new_sz 函数初始化 constexpr 类型的变量 fsz。
执行该初始化任务时,编译器把对 constexpr 函数的调用替换成其结果值。为了能在编译过程中随时展开,constexpr 函数被隐式地指定为内联函数。
constexpr 函数体内也可以包含其他语句,只要这些语句在运行时不执行任何操作就行。例如,constexpr 函数中可以有空语句、类型别名以及 using 声明。
我们允许 constexpr 函数的返回值并非一个常量:
constexpr int new_sz() { return 42;}
// 如果 cnt 是常量表达式,则 scale(cnt) 也是常量表达式
constexpr size_t scale(size_t cnt) { return new_sz()*cnt; }
int arr[scale(2)]; // 正确:scale(2) 是常量表达式
int i = 2; // i 不是常量表达式
int a2[scale(i)]; // 错误:scale(i) 不是常量表达式
当把 scale 函数用在需要常量表达式的代码块中时,由编译器负责检查函数的结果是否符合要求。如果结果恰好不是常量表达式,编译器将发出错误信息。
所以说,constexpr 函数不一定返回常量表达式。
constexpr 构造函数
尽管构造函数不能是 const 的,但是字面值常量类的构造函数可以是 constexpr 函数。事实上,一个字面值常量类必须至少提供一个 constexpr 构造函数。
constexpr 构造函数可以声明成 = default 的形式(或是删除函数的形式)。否则,constexpr 构造函数就必须既符合构造函数的要求,又符合 constexpr 构造的要求(意味着它能拥有的唯一可执行语句就是返回语句)。综合这两点可知,constexpr 构造函数体一般来说应该是空的。我们通过前置关键字 constexpr 就可以声明一个 constexpr 构造函数了:
class Debug {
public:
constexpr Debug(bool b = true) : hardwareError(b), ioError(b), otherError(b) {}
constexpr Debug(bool hw, bool io, bool ot) : hardwareError(hw), ioError(io), otherError(ot) {}
constexpr bool error() { return hardwareError || ioError || otherError;}
void setIOError(bool is) { ioError = is; }
void setHardwareError(bool is) { hardwareError = is; }
void setOtherError(bool is) { otherError = is; }
private:
bool hardwareError;
bool ioError;
bool otherError;
};
constexpr 构造函数必须初始化所有数据成员,初始化或者使用 constexpr 构造函数,或者是一条常量表达式。
constexpr 构造函数用于生成 constexpr 对象以及 constexpr 函数的参数或返回类型:
constexpr Debug io_sub(false, true, false);
if (io_sub.error()) {
std::cerr << "There are some error messages ..." << std::endl;
}
constexpr Debug prod(false);
if (prod.error()) { // 等价于 if (false)
std::cerr << "There is no error messages ..." << std::endl;
}
网友评论