template<int N>
class fixed_size_list
{ /*...*/ };
fixed_size_list<X> mylist; // X must be an integer constant expression
int numbers[X]; // X must be an integer constant expression
如果 function 适合在常量表达式中使用,则 must 应显式声明为 constexpr ;仅仅满足恒定表达函数的标准是不够的 . 例:
template<int N>
class list
{ };
constexpr int sqr1(int arg)
{ return arg * arg; }
int sqr2(int arg)
{ return arg * arg; }
int main()
{
const int X = 2;
list<sqr1(X)> mylist1; // OK: sqr1 is constexpr
list<sqr2(X)> mylist2; // wrong: sqr2 is not constexpr
}
When can I / should I use both, const and constexpr together?
A. In object declarations. 当两个关键字都引用要声明的同一对象时,这是绝对必要的 . constexpr 暗示 const .
constexpr const int N = 5;
是相同的
constexpr int N = 5;
但请注意,可能存在关键字各自引用声明的不同部分的情况:
static constexpr int N = 3;
int main()
{
constexpr const int *NP = &N;
}
constexpr int max() { return INT_MAX; } // OK
constexpr long long_max() { return 2147483647; } // OK
constexpr bool get_val()
{
bool res = false;
return res;
} // error: body is not just a return statement
constexpr int square(int x)
{ return x * x; } // OK: compile-time evaluation only if x is a constant expression
const int res = square(5); // OK: compile-time evaluation of square(5)
int y = getval();
int n = square(y); // OK: runtime evaluation of square(y)
struct S
{
constexpr int two(); // constant-expression function
private:
static constexpr int sz; // constant-expression object
};
constexpr int S::sz = 256;
enum DataPacket
{
Small = S::two(), // error: S::two() called before it was defined
Big = 1024
};
constexpr int S::two() { return sz*2; }
constexpr S s;
int arr[s.two()]; // OK: s.two() called after its definition
const int dmv = 17; // dmv is a named constant
int var = 17; // var is not a constant
constexpr double max1 = 1.4*square(dmv); // OK if square(17) is a constant expression
constexpr double max2 = 1.4∗square(var); // error : var is not a constant expression
const double max3 = 1.4∗square(var); //OK, may be evaluated at run time
double sum(const vector<double>&); // sum will not modify its argument (§2.2.5)
vector<double> v {1.2, 3.4, 4.5}; // v is not a constant
const double s1 = sum(v); // OK: evaluated at run time
constexpr double s2 = sum(v); // error : sum(v) not constant expression
int temp=rand(); // temp is generated by the the random generator at runtime.
const int x1=10; // OK - known at compile time.
const int x2=temp; // OK - known only at runtime.
constexpr int x3=10; // OK - known at compile time.
constexpr int x4=temp; // ERROR. Compiler can't figure out the value of 'temp' variable at compile time so `constexpr` can't be applied here.
int temp=rand(); // temp is generated by the the random generator at runtime.
int array1[10]; // OK.
int array2[temp]; // ERROR.
所以它意味着:
const int size1=10; // OK - value known at compile time.
const int size2=temp; // OK - value known only at runtime.
constexpr int size3=10; // OK - value known at compile time.
int array3[size1]; // OK - size is known at compile time.
int array4[size2]; // ERROR - size is known only at runtime time.
int array5[size3]; // OK - size is known at compile time.
class test
{
int x;
void function1()
{
x=100; // OK.
}
void function2() const
{
x=100; // ERROR. The const methods can't change the values of object fields.
}
};
constexpr 是一个不同的概念 . 它将函数(成员或非成员)标记为可在编译时 if compile time constants are passed as their arguments 评估的函数 . 例如,你可以写这个 .
constexpr int func_constexpr(int X, int Y)
{
return(X*Y);
}
int func(int X, int Y)
{
return(X*Y);
}
int array1[func_constexpr(10,20)]; // OK - func_constexpr() can be evaluated at compile time.
int array2[func(10,20)]; // ERROR - func() is not a constexpr function.
int array3[func_constexpr(10,rand())]; // ERROR - even though func_constexpr() is the 'constexpr' function, the expression 'constexpr(10,rand())' can't be evaluated at compile time.
int value1=func_constexpr(10,rand()); // OK. value1 is non-constexpr value that is evaluated in runtime.
constexpr int value2=func_constexpr(10,rand()); // ERROR. value2 is constexpr and the expression func_constexpr(10,rand()) can't be evaluated at compile time.
constexpr 也可以应用于成员函数(方法),运算符甚至构造函数 . 例如 .
class test2
{
static constexpr int function(int value)
{
return(value+1);
}
void f()
{
int x[function(10)];
}
};
更“疯狂”的样本 .
class test3
{
public:
int value;
// constexpr const method - can't chanage the values of object fields and can be evaluated at compile time.
constexpr int getvalue() const
{
return(value);
}
constexpr test3(int Value)
: value(Value)
{
}
};
constexpr test3 x(100); // OK. Constructor is constexpr.
int array[x.getvalue()]; // OK. x.getvalue() is constexpr and can be evaluated at compile time.
cout << "Enter your age: ";
int age;
cin >> age;
const int myAge{age}; // works
constexpr int someAge{age}; // error: age can only be resolved at runtime
26
const int var 可以在运行时动态设置为一个值,一旦设置为该值,就不能再更改它 .
constexpr int var 不能在运行时动态设置,而是在编译时动态设置 . 一旦设置为该值,就不能再进行更改 .
这是一个很好的例子:
int main(int argc, char*argv[]) {
const int p = argc;
// p = 69; // cannot change p because it is a const
// constexpr int q = argc; // cannot be, bcoz argc cannot be computed at compile time
constexpr int r = 2^3; // this works!
// r = 42; // same as const too, it cannot be changed
}
7 回答
基本含义和语法
两个关键字都可以用于对象和函数的声明 . 应用于对象时的基本区别是:
const
将对象声明为常量 . 这意味着保证一旦初始化,该对象的值不会改变,并且编译器可以利用这个事实进行优化 . 它还有助于防止程序员编写修改初始化后无意修改的对象的代码 .constexpr
声明一个对象适合在标准调用常量表达式中使用 . 但请注意constexpr
并不是唯一的方法 .应用于函数时,基本区别在于:
const
只能用于非静态成员函数,而不能用于一般函数 . 它保证成员函数不会修改任何非静态数据成员 .constexpr
可以与成员函数和非成员函数以及构造函数一起使用 . 它声明该函数适合在常量表达式中使用 . 如果函数满足某些标准(7.1.5 / 3,4),编译器将只接受它,最重要的是(†):函数体必须是非虚拟的并且非常简单:除了typedef和static asserts之外,只允许一个
return
语句 . 对于构造函数,只允许初始化列表,typedef和static assert . (= default
和= delete
也是允许的 . )从C14开始,规则更加宽松,从那时起在constexpr函数中允许的内容:
asm
声明,goto
语句,带有case
和default
以外的标签的语句,try-block,非变量的定义文字类型,静态或线程存储持续时间变量的定义,未执行初始化的变量的定义 .参数和返回类型必须是文字类型(即,一般来说,非常简单的类型,通常是标量或聚合)
常量表达式
如上所述,
constexpr
声明了两个对象以及适合在常量表达式中使用的函数 . 常量表达式不仅仅是常量:但请注意:
将某些内容声明为
constexpr
并不一定能保证在编译时对其进行评估 . 它可以用于此类,但它也可以在运行时评估的其他地方使用 .一个对象可能适合在常量表达式中使用而不被声明为
constexpr
. 例:这是可能的,因为
N
,在声明时使用文字进行常量和初始化,满足常量表达式的条件,即使它未被声明为constexpr
.So when do I actually have to use constexpr?
object 如上面的
N
可用作常量表达式而不被声明为constexpr
. 对于以下所有对象都是如此:const
的整数或枚举类型和
在声明时初始化,表达式本身就是一个常量表达式
[这是由于§5.19/ 2:常量表达式不得包含涉及“左值到右值修改的子表达式,除非[...]积分或枚举类型的glvalue [...]”感谢Richard Smith纠正我的早先声称所有文字类型都适用 . ]
constexpr
;仅仅满足恒定表达函数的标准是不够的 . 例:When can I / should I use both, const and constexpr together?
A. In object declarations. 当两个关键字都引用要声明的同一对象时,这是绝对必要的 .
constexpr
暗示const
.是相同的
但请注意,可能存在关键字各自引用声明的不同部分的情况:
这里,
NP
被声明为地址常量表达式,即指针本身是一个常量表达式 . (当通过将地址运算符应用于a来生成地址时,这是可能的static / global constant expression . )这里,constexpr
和const
都是必需的:constexpr
总是引用被声明的表达式(这里是NP
),而const
引用int
(它声明指向const的指针) . 删除const
会使表达式变为非法(因为(a)指向非const对象的指针不能是常量表达式,而(b)&N
实际上是指向常量的指针) .B. In member function declarations. 在C 11中,
constexpr
暗示const
,而在C 14和C 17中则不是这种情况 . 在C 11下声明的成员函数需要声明为
在C 14下,仍然可以用作
const
功能 .const
适用于您的代码中的 variables 和 prevents them from being modified .constexpr
告诉编译器这个 expression 会产生 compile time constant value ,所以它可以在数组长度,分配给const
变量等的地方使用.Oli给出的link有很多很好的例子 .基本上它们完全是两个不同的概念,并且可以(并且应该)一起使用 .
概述
const
保证程序 does not change an object’s value . 但是,const
不保证对象经历哪种类型的初始化 .考虑:
函数
max()
仅返回文字值 . 但是,由于初始化程序是函数调用,mx
会进行运行时初始化 . 因此,您不能将其用作常量表达式:constexpr
是一个新的C 11关键字,它使您无需创建宏和硬编码文字 . 在某些条件下,它还保证对象经历静态初始化 . 它控制表达式的评估时间 . 通过强制执行 compile-time evaluation of its expression ,constexpr
允许您定义真正的常量表达式,这些表达式对于时间关键型应用程序,系统编程,模板以及一般来说,依赖于编译时常量的任何代码都至关重要 .常量表达式函数
常量表达式函数是声明为
constexpr
的函数 . 它的主体必须是非虚拟的,除了typedef和静态断言之外,它只包含一个return语句 . 它的参数和返回值必须具有文字类型 . 它可以与非常量表达式参数一起使用,但是当完成时,结果不是常量表达式 .常量表达式函数旨在替换宏和硬编码文字,而不会牺牲性能或类型安全性 .
常量表达式对象
常量表达式对象是声明为
constexpr
的对象 . 必须使用常量表达式或由具有常量表达式参数的常量表达式构造函数构造的rvalue初始化它 .常量表达式对象的行为就像它被声明为
const
一样,除了它在使用前需要初始化并且它的初始值设定项必须是常量表达式 . 因此,常量表达式对象始终可以用作另一个常量表达式的一部分 .常量表达式构造函数
常量表达式构造函数是声明为
constexpr
的构造函数 . 它可以有一个成员初始化列表,但除了typedef和static asserts之外,它的主体必须是空的 . 它的参数必须有文字类型 .如果构造函数的参数都是常量表达式,则常量表达式构造函数允许编译器在编译时初始化对象 .
来自Scott Meyers的书“Effective Modern C”中的提示
constexpr
:constexpr
对象是const,并使用编译期间已知的值进行初始化;constexpr
函数在使用参数调用时产生编译时结果,这些参数的值在编译期间是已知的;constexpr
对象和函数可用于比非对象和函数更广泛的上下文中;constexpr
是对象或函数接口的一部分 .资料来源:Using constexpr to Improve Security, Performance and Encapsulation in C++ .
根据Bjarne Stroustrup的"The C++ Programming Language 4th Editon"一书
• const :意思是“我保证不会改变这个值”(§7.5) . 这主要用于指定接口,以便可以将数据传递给函数,而不必担心它们被修改 .
编译器强制执行const的承诺 .
• constexpr :意思是“在编译时大致''进行评估'(第10.4节) . 这主要用于指定允许的常量
例如:
对于可在常量表达式中使用的函数,即在将由编译器计算的表达式中,必须将其定义为 constexpr .
例如:
要成为constexpr,函数必须相当简单:只需一个计算值的return语句 . constexpr函数可以用于非常量参数,但是当完成时,结果不是常量表达式 . 我们允许在不需要常量表达式的上下文中使用非常量表达式参数调用constexpr函数,因此我们不能两次定义基本相同的函数:一次用于常量表达式,一次用于变量 .
在少数地方,语言规则需要常量表达式(例如,数组边界(§2.2.5,§7.3),案例标签(§2.2.4,§9.4.2),一些模板参数(§25.2),以及使用constexpr声明的常量) . 在其他情况下,编译时评估对性能很重要 . 与性能问题无关,不可变性(具有不可更改状态的对象)的概念是一个重要的设计问题(第10.4节) .
const
和constexpr
都可以应用于变量和函数 . 尽管它们彼此相似,但事实上它们是非常不同的概念 .const
和constexpr
都表示在初始化后无法更改其值 . 例如:const
和constexpr
之间的主要区别是它们的初始化值已知(已评估)的时间 . 虽然可以在编译时和运行时计算const
变量的值,但始终在编译时计算constexpr
. 例如:知道在编译时或运行时是否知道值的关键优势是,只要需要编译时常量,就可以使用编译时常量 . 例如,C不允许您指定具有可变长度的C数组 .
所以它意味着:
所以
const
变量可以定义 compile time constants ,如size1
,可用于指定数组大小, runtime constants ,如size2
,仅在运行时已知,不能用于定义数组大小 . 另一方面,constexpr
始终定义可以指定数组大小的编译时常量 .const
和constexpr
也可以应用于函数 .const
函数必须是成员函数(方法,运算符),其中const
关键字的应用意味着该方法无法更改其成员(非静态)字段的值 . 例如 .constexpr
是一个不同的概念 . 它将函数(成员或非成员)标记为可在编译时 if compile time constants are passed as their arguments 评估的函数 . 例如,你可以写这个 .顺便说一句,
constexpr
函数是常规的C函数,即使传递非常量参数也可以调用它们 . 但在这种情况下,您将获得非constexpr值 .constexpr
也可以应用于成员函数(方法),运算符甚至构造函数 . 例如 .更“疯狂”的样本 .
正如@ 0x499602d2已经指出的那样,
const
仅确保在初始化之后无法更改值,因为constexpr
(在C11中引入)保证变量是编译时常量 .请考虑以下示例(来自LearnCpp.com):
const int var
可以在运行时动态设置为一个值,一旦设置为该值,就不能再更改它 .constexpr int var
不能在运行时动态设置,而是在编译时动态设置 . 一旦设置为该值,就不能再进行更改 .这是一个很好的例子:
上面的代码片段编译得很好,我已经注释掉导致它出错的那些 .