constexpr 关键字的功能是使指定的常量表达式获得在程序编译阶段计算出结果的能力,而不必等到程序运行阶段。
constexpr 可用于修饰普通变量、函数(包括模板函数)以及类的构造函数。
1 修饰普通变量, num在编译时就确定是6
static void testConstexprValue() {
constexpr int num = 1 + 2 + 3;
int url[num] = {1,2,3,4,5,6};
std::cout<< url[1] << std::endl;
}
2 constexpr修饰函数:
1) 整个函数的函数体中,除了可以包含 using 指令、typedef 语句以及 static_assert 断言外,只能包含一条 return 返回语句。
constexpr int display(int x) {
int ret = 1 + 2 + x; // 这个函数无法编译通过。
return ret;
}
2) 该函数必须有返回值,即函数的返回值类型不能是 void。
3) 函数在使用之前,必须有对应的定义语句。我们知道,函数的使用分为“声明”和“定义”两部分, 普通的函数调用只需要提前写好该函数的声明部分即可(函数的定义部分可以放在调用位置之后甚至其它文件中),但常量表达式函数在使用前,必须要有该函数的定义。
4) return 返回的表达式必须是常量表达式
int num = 3;
constexpr int display(int x){
return num + x;
}
int main()
{
//调用常量表达式函数
int a[display(3)] = { 1,2,3,4 };
return 0;
}
该程序无法通过编译,编译器报“display(3) 的结果不是常量”的异常。
常量表达式函数的返回值必须是常量表达式的原因很简单,如果想在程序编译阶段获得某 个函数返回的常量,则该函数的 return 语句中就不能包含程序运行阶段才能确定值的变量。
3 constexpr 自定义类型的定义
struct myType {
constexpr myType(char *name,int age):name(name),age(age){};
const char* name;
int age;
//其它结构体成员
};
int main()
{
constexpr struct myType mt { "zhangsan", 10 };
cout << mt.name << " " << mt.age << endl;
return 0;
}
此程序是无法通过编译的,编译器会抛出“constexpr不能修饰自定义类型”的异常。 当我们想自定义一个可产生常量的类型时,正确的做法是在该类型的内部添加一个常量构造函数。
例如,修改上面的错误示例如下
//自定义类型的定义
struct myType {
constexpr myType(char *name,int age):name(name),age(age){};
const char* name;
int age;
//其它结构体成员
};
这样就可以编译通过
4 constexpr模板:
//自定义类型的定义
struct myType1 {
const char* name;
int age;
//其它结构体成员
};
//模板函数
template<typename T>
constexpr T dispaly1(T t){
return t;
}
void testConstexprTemplate()
{
struct myType1 stu{"zhangsan",10};
//普通函数
struct myType1 ret = dispaly1(stu);
std::cout << ret.name << " " << ret.age << std::endl;
//常量表达式函数
constexpr int ret1 = dispaly1(10);
std::cout << ret1 << std::endl;
}
可以看到,示例程序中定义了一个模板函数 display1(),但由于其返回值类型未定,因此在实例化之前无法判断其是否符合常量表达式函数的要求:
当模板函数中以自定义结构体 myType1 类型进行实例化时,由于该结构体中没有定义常量表达式构造函数,所以实例化后的函数不是常量表达式函数,此时 constexpr 是无效的;
模板函数的类型 T 为 int 类型,实例化后的函数符合常量表达式函数的要求,所以该函数的返回值就是一个常量表达式。