constexpr和const之间有什么区别?
我什么时候只能使用其中一个?我什么时候可以同时使用这两种方法,我应该如何选择一种?
constexpr和const之间有什么区别?
我什么时候只能使用其中一个?我什么时候可以同时使用这两种方法,我应该如何选择一种?
当前回答
const和constexpr关键字概述
在C++中,如果用常量表达式初始化常量对象,我们可以在需要常量表达式的地方使用常量对象。
const int x = 10;
int a[x] = {0};
例如,我们可以在switch中使用case语句。
constexpr可以与数组一起使用。
constexpr不是类型。
constexpr关键字可以与auto关键字一起使用。
constexpr auto x = 10;
struct Data { // We can make a bit field element of struct.
int a:x;
};
如果我们用常量表达式初始化常量对象,那么该常量对象生成的表达式现在也是常量表达式。
常量表达式:可以在编译时计算其值的表达式。
x*5-4//这是一个常量表达式。对于编译器,键入此表达式和直接键入46之间没有区别。
初始化是必需的。它只能用于阅读目的。无法更改。到目前为止,“const”和“constexpr”关键字之间没有区别。
注意:我们可以在同一声明中使用constexpr和const。
constexpr const int* p;
Constexpr函数
通常,函数的返回值是在运行时获得的。但当满足某些条件时,对constexpr函数的调用将在编译时作为常量获得。
注意:在函数调用中发送给函数的参数变量的参数,如果有多个参数,则发送给所有参数变量,如果是C。E,则函数的返回值将在编译时计算!!!
constexpr int square (int a){
return a*a;
}
constexpr int a = 3;
constexpr int b = 5;
int arr[square(a*b+20)] = {0}; //This expression is equal to int arr[35] = {0};
为了使函数成为constexpr函数,函数的返回值类型和函数参数的类型必须在名为“literal type”的类型类别中。
constexpr函数是隐式内联函数。
重要一点:
无需使用常量表达式调用constexpr函数。它不是必需的。如果发生这种情况,计算将不会在编译时完成。它将被视为正常的函数调用。因此,当需要常量表达式时,我们将无法再使用此表达式。
constexpr函数所需的条件如下所示:;
1)函数参数中使用的类型和函数返回值的类型必须是文本类型。
2)函数内部不应使用具有静态寿命的局部变量。
3)如果函数是合法的,当我们在编译时使用常量表达式调用此函数时,编译器会在编译时计算函数的返回值。
4)编译器需要查看函数的代码,因此constexpr函数几乎总是在头文件中。
5)为了使我们创建的函数成为constexpr函数,函数的定义必须在头文件中。因此,无论哪个源文件包含该头文件,都将看到函数定义。
奖金
通常使用默认成员初始化,可以在类中初始化具有常量和整数类型的静态数据成员。然而,为了做到这一点,必须同时存在“常量”和“整型”。
如果我们使用静态constexpr,那么它不必是一个整型来在类中初始化它。只要用常量表达式初始化它,就没有问题。
class Myclass {
const static int sx = 15; // OK
constexpr static int sy = 15; // OK
const static double sd = 1.5; // ERROR
constexpr static double sd = 1.5; // OK
};
其他回答
基本含义和语法
这两个关键字都可以用于对象和函数的声明。应用于对象时的基本区别如下:
const将对象声明为常量。这意味着保证一旦初始化,该对象的值不会改变,编译器可以利用这一事实进行优化。它还有助于防止程序员编写代码来修改初始化后不打算修改的对象。constexpr声明一个对象适合在标准所称的常量表达式中使用。但请注意,constexpr并不是唯一的方法。
应用于函数时,基本区别如下:
const只能用于非静态成员函数,而不能用于一般函数。它保证成员函数不会修改任何非静态数据成员(可变数据成员除外,无论如何都可以修改)。constexpr可以与成员函数和非成员函数以及构造函数一起使用。它声明函数适合在常量表达式中使用。只有当函数满足某些标准(7.1.5/3,4),最重要的是(†),编译器才会接受它:函数体必须是非虚拟的并且非常简单:除了typedef和静态断言之外,只允许一个return语句。对于构造函数,只允许使用初始化列表、typedef和静态断言。(不过,也允许使用=default和=delete。)从C++14开始,规则更加宽松,从那时起,在constexpr函数中允许这样做:asm声明、goto语句、带有除case和default之外的标签的语句、try块、非文字类型变量的定义、静态或线程存储持续时间变量的定义,以及不执行初始化的变量的定义。参数和返回类型必须是文本类型(即,一般来说,非常简单的类型,通常是标量或聚合)
常量表达式
如上所述,constexpr声明了适合在常量表达式中使用的对象和函数。常量表达式不仅仅是常量:
它可以用于需要编译时评估的地方,例如模板参数和数组大小说明符:模板<int N>类fixed_size_list{ /*...*/ };fixed_size_list<X>mylist;//X必须是整数常量表达式整数[X];//X必须是整数常量表达式但请注意:将某物声明为constexpr并不一定保证在编译时对其求值。它可以用于这种情况,但也可以用于运行时评估的其他地方。对象可以适合在常量表达式中使用,而不必声明为constexpr。例子:int main(){常量int N=3;整数[N]={1,2,3};//N是常量表达式}这是可能的,因为N是常量,并且在声明时用文本初始化,满足常量表达式的条件,即使它没有声明为constexpr。
那么我什么时候真的必须使用constexpr?
像上面N这样的对象可以用作常量表达式,而不必声明为constexpr。这适用于以下所有对象:常量整数或枚举类型,以及在声明时使用本身为常量表达式的表达式初始化
[这是由于§5.19/2:常量表达式不能包含涉及“左值到右值修改,除非[…]是整数或枚举类型的glvalue[…]”的子表达式。感谢理查德·史密斯纠正了我之前的说法,即这适用于所有文字类型。]
对于适合在常量表达式中使用的函数,必须显式声明为constexpr;仅仅满足常数表达式函数的标准是不够的。例子:模板<int N>类别列表{ };常量表达式int sqr1(int arg){return arg*arg;}整数sqr2(整数参数){return arg*arg;}int main(){常量int X=2;列表<sqr1(X)>mylist1;//确定:sqr1是常量表达式列表<sqr2(X)>mylist2;//错误:sqr2不是constexpr}
我什么时候可以/应该同时使用const和constexpr?
A.在对象声明中。当两个关键字都引用要声明的同一对象时,这从来都不是必需的。constexpr表示常量。
constexpr const int N = 5;
与
constexpr int N = 5;
但是,请注意,在某些情况下,每个关键字都引用声明的不同部分:
static constexpr int N = 3;
int main()
{
constexpr const int *NP = &N;
}
这里,NP被声明为一个地址常量表达式,即一个本身就是常量表达式的指针。(当通过将地址运算符应用于静态/全局常量表达式来生成地址时,这是可能的。)在这里,constexpr和const都是必需的:constexpr始终引用要声明的表达式(这里是NP),而const引用int(它声明指向const的指针)。删除常量将使表达式非法(因为(a)指向非常量对象的指针不能是常量表达式,而(b)&N实际上是指向常量的指针)。
B.在成员函数声明中。在C++11中,constexpr表示const,而在C++14和C++17中则不是这样。在C++11中声明为
constexpr void f();
需要声明为
constexpr void f() const;
以便仍然可用作常量函数。
const和constexpr关键字概述
在C++中,如果用常量表达式初始化常量对象,我们可以在需要常量表达式的地方使用常量对象。
const int x = 10;
int a[x] = {0};
例如,我们可以在switch中使用case语句。
constexpr可以与数组一起使用。
constexpr不是类型。
constexpr关键字可以与auto关键字一起使用。
constexpr auto x = 10;
struct Data { // We can make a bit field element of struct.
int a:x;
};
如果我们用常量表达式初始化常量对象,那么该常量对象生成的表达式现在也是常量表达式。
常量表达式:可以在编译时计算其值的表达式。
x*5-4//这是一个常量表达式。对于编译器,键入此表达式和直接键入46之间没有区别。
初始化是必需的。它只能用于阅读目的。无法更改。到目前为止,“const”和“constexpr”关键字之间没有区别。
注意:我们可以在同一声明中使用constexpr和const。
constexpr const int* p;
Constexpr函数
通常,函数的返回值是在运行时获得的。但当满足某些条件时,对constexpr函数的调用将在编译时作为常量获得。
注意:在函数调用中发送给函数的参数变量的参数,如果有多个参数,则发送给所有参数变量,如果是C。E,则函数的返回值将在编译时计算!!!
constexpr int square (int a){
return a*a;
}
constexpr int a = 3;
constexpr int b = 5;
int arr[square(a*b+20)] = {0}; //This expression is equal to int arr[35] = {0};
为了使函数成为constexpr函数,函数的返回值类型和函数参数的类型必须在名为“literal type”的类型类别中。
constexpr函数是隐式内联函数。
重要一点:
无需使用常量表达式调用constexpr函数。它不是必需的。如果发生这种情况,计算将不会在编译时完成。它将被视为正常的函数调用。因此,当需要常量表达式时,我们将无法再使用此表达式。
constexpr函数所需的条件如下所示:;
1)函数参数中使用的类型和函数返回值的类型必须是文本类型。
2)函数内部不应使用具有静态寿命的局部变量。
3)如果函数是合法的,当我们在编译时使用常量表达式调用此函数时,编译器会在编译时计算函数的返回值。
4)编译器需要查看函数的代码,因此constexpr函数几乎总是在头文件中。
5)为了使我们创建的函数成为constexpr函数,函数的定义必须在头文件中。因此,无论哪个源文件包含该头文件,都将看到函数定义。
奖金
通常使用默认成员初始化,可以在类中初始化具有常量和整数类型的静态数据成员。然而,为了做到这一点,必须同时存在“常量”和“整型”。
如果我们使用静态constexpr,那么它不必是一个整型来在类中初始化它。只要用常量表达式初始化它,就没有问题。
class Myclass {
const static int sx = 15; // OK
constexpr static int sy = 15; // OK
const static double sd = 1.5; // ERROR
constexpr static double sd = 1.5; // OK
};
根据Bjarne Stroustrup的《C++编程语言第四版》一书•const:大致意思是“我保证不会改变这个值”(§7.5)。这主要用于指定接口,以便可以将数据传递给函数,而不必担心数据被修改。编译器执行const的承诺。•constexpr:大致意思是“在编译时要求值”(§10.4)。这主要用于指定常量,以允许例如:
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
函数在常量表达式中可用,即在要计算的表达式中编译器必须将其定义为constexpr。例如:
constexpr double square(double x) { return x∗x; }
要成为constexpr,函数必须相当简单:只需要一个计算值的return语句。A.constexpr函数可以用于非常量参数,但当这样做时,结果不是常量表达式。我们允许使用非常量表达式参数调用constexpr函数在不需要常量表达式的上下文中,这样我们就不需要定义相同的函数两次:一次用于常量表达式,一次用于变量。在一些地方,语言规则需要常量表达式(例如,数组边界(§2.2.5,§7.3)、案例标签(§2.2.4、§9.4.2)、一些模板参数(§25.2)以及使用常量表达式)。在其他情况下,编译时评估对性能很重要。独立于性能问题,不变性(具有不可更改状态的对象)的概念是重要设计问题(§10.4)。
正如@0x499602d2已经指出的,const只确保初始化后不能更改值,而constexpr(在C++11中引入)保证变量是编译时常量。考虑以下示例(来自LearnApp.com):
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
const和constexpr都可以应用于变量和函数。尽管它们彼此相似,但实际上它们是非常不同的概念。
const和constexpr都意味着它们的值在初始化后不能更改。例如:
const int x1=10;
constexpr int x2=10;
x1=20; // ERROR. Variable 'x1' can't be changed.
x2=20; // ERROR. Variable 'x2' can't be changed.
const和constexpr之间的主要区别是它们的初始化值已知(求值)的时间。虽然const变量的值可以在编译时和运行时计算,但constexpr始终在编译时计算。例如:
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.
了解该值在编译时或运行时是否已知的关键优势是,只要需要编译时常数,就可以使用编译时常数。例如,C++不允许您指定长度可变的C数组。
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.
因此,常量变量可以定义编译时常量(如size1)和运行时常量(例如size2),前者可以用于指定数组大小,后者只能在运行时知道,不能用于定义数组大小。另一方面,constexpr总是定义可以指定数组大小的编译时常数。
const和constexpr也可以应用于函数。const函数必须是成员函数(方法、运算符),其中应用const关键字意味着该方法不能更改其成员(非静态)字段的值。例如
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是一个不同的概念。它将函数(成员或非成员)标记为可以在编译时求值的函数,如果编译时常量作为其参数传递。例如,你可以写这个。
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.
顺便说一句,constexpr函数是常规C++函数,即使传递了非常量参数,也可以调用这些函数。但在这种情况下,您得到的是非常量表达式值。
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.