C++ 规格要求数组大小的定义上,以及枚举值(enumerator values)都要求必须是编译器常数表示式:
int getCount()
{
return 5;
}
const int count1 = 5;
const int count3 = getCount();
int count2 = 5;
int a1[5];
int a2[count1];
int a3[count2]; //compile error, 这不是合法的 C++, count2非编译期常数
int a4[getCount()]; //compile error, 这不是合法的 C++, getCount()非编译期常数
int a5[count3]; //cimpile error, 这不是合法的 C++, count3非编译期常数
enum V1
{
ENUM_V1_A = count1,
ENUM_V1_B,
};
enum V2
{
ENUM_V2_A = count2, //compile error, 这不是合法的 C++, count2非编译期常数
ENUM_V2_B,
};
然而实际上getCount()和count2 总是产生相同的结果,但编译器无法得知。 理论上而言,这个函数可能会影响全局参数,或者调用其他的非运行期(non-runtime)常数函数等。C++11引进关键字 constexpr 允许用户保证函数或是对象建构式是编译期常数。以上的例子可以被写成像是下面这样:
constexpr int getCount()
{
return 5;
}
const int count1 = 5;
const int count3 = getCount();
constexpr int count2 = 5;
int a1[5];
int a2[count1];
int a3[count2];
int a4[getCount()];
int a5[count3];
enum V1
{
ENUM_V1_A = count1,
ENUM_V1_B,
};
enum V2
{
ENUM_V2_A = count2,
ENUM_V2_B,
};
这使得编译器能够了解并去验证 getCount()和count2 是个编译期常数。
常函数必须满足如下标准(647 号议题,2008.6):
- 非虚函数。
- 返回值和参数表都须为“字面类型(Literal Type)”。
- 函数体只能是 return expression; 的形式,expression 应为潜在的常数表达式。
- 函数体被声明为 constexpr。
- 由 expression 到返回类型的隐式转化(implicit conversion)须为常表达式允许的转化形式之一。
首先,该函数的回返值类型不能为 void。
第二点,函数的内容必须依照 "return expr" 的形式。
第三点,在引数取代后,expr 必须是个常数表示式。这些常数表示式只能够调用其他被定义为 constexpr 的函数,或是其他常数表示式的数据参数。
extern constexpr int doSomething5();
int a[doSomething5()]; //Compile error4, 当前位置doSomething5()没有定义
constexpr void doSomething1() //Compile error1, 返回值不能为void
{
}
constexpr int doSomething2() //Compile error2, 返回值不为常量表达式
{
int a = 5;
return a + 5;
}
const int COUNT = 5;
constexpr int doSomething3() //返回值为常量表达式
{
return COUNT + 5;
}
constexpr int doSomething5() //返回值为常量表达式
{
return 5;
}
int b[doSomething5()]; //当前位置doSomething5()有定义
constexpr int doSomethings4() //返回值为常量表达式
{
return doSomething3() + 5;
}
constexpr int doSomething7(int v) {
return v + 5;
}
int main(int argc, const char * argv[])
{
constexpr int v1 = doSomething7(1);
constexpr int v2 = doSomething7(argc);//compile error, argc不为常量表达式
return 0;
}
为了从用户自定类型(user-defined type)建构常数表示式的数据参数,构造函数也可以被声明成 constexpr。与常数表示式函数一样,常数表示式的构造函数必须在该编译单元内使用之前被定义。它必须有着空的函数本体。它必须用常数表示式初始化他的成员(member)。而这种类型的析构式应当是无意义的(trivial),什么事都不做。
struct Point
{
int x, y;
constexpr Point(int xx, int yy) : x(xx), y(yy) {}
};
int main()
{
int a = 0, b = 0;
constexpr Point origo(0, 0);
constexpr int z = origo.x;
constexpr Point origo(a, b); //error
return 0;
}
复制 constexpr 建构起来的类型也应该被定义为 constexpr,这样可以让他们从常数表示式的函数以值传回。类型的任何成员函数,像是复制重载的运算符等等,只要他们符合常数表示式函数的定义,都可以被声明成 constexpr。这使得编译器能够在编译期进行类型的复制、对他们施行运算等等。常数表示式函数或建构式,可以以非常数表示式(non-constexpr)参数唤起。就如同 constexpr 整数字面值能够指派给 non-constexpr 参数,constexpr 函数也可以接受 non-constexpr 参数,其结果存储于 non-constexpr 参数。constexpr 关键字只有当表示式的成员都是 constexpr,才允许编译期常数性的可能。
转载自: http://blog.csdn.net/liqinghua1653/article/details/12293415