차이 무엇 constexpr과 const?
- 언제 하나만 사용할 수 있습니까?
- 둘 다 언제 사용할 수 있으며 어떻게 선택해야합니까?
boost/hana라이브러리 에서이 기사를 constexpr사용 constexpr하여 사용할 수없고 사용할 수없는 일부 문제점을 밝힐 수 있습니다 . boost.org/doc/libs/1_69_0/libs/hana/doc/html/…
constexpr
차이 무엇 constexpr과 const?
boost/hana라이브러리 에서이 기사를 constexpr사용 constexpr하여 사용할 수없고 사용할 수없는 일부 문제점을 밝힐 수 있습니다 . boost.org/doc/libs/1_69_0/libs/hana/doc/html/…
constexpr
답변:
두 키워드는 함수뿐만 아니라 객체 선언에도 사용할 수 있습니다. 객체에 적용 할 때의 기본적인 차이점 은 다음과 같습니다.
const객체를 constant 로 선언합니다 . 이는 일단 초기화되면 해당 객체의 값이 변경되지 않으며 컴파일러가이 사실을 최적화에 활용할 수 있음을 보증합니다. 또한 프로그래머가 초기화 후에 수정되지 않은 객체를 수정하는 코드를 작성하지 못하게합니다.
constexprStandard가 상수 표현식을 호출하는 데 사용하기에 적합한 객체를 선언합니다 . 그러나 constexpr이것이 유일한 방법은 아닙니다.
함수에 적용 할 때 기본적인 차이점은 다음과 같습니다.
const비 정적 멤버 함수에만 사용할 수 있으며 일반적인 함수는 사용할 수 없습니다. 멤버 함수가 비 정적 데이터 멤버를 수정하지 않음을 보증합니다.
constexpr멤버 함수와 비 멤버 함수 및 생성자 모두와 함께 사용할 수 있습니다. 상수 표현식 에 사용하기에 적합한 함수를 선언합니다 . 컴파일러는 함수가 특정 기준 (7.1.5 / 3,4), 가장 중요하게 (†)를 충족하는 경우에만 수락합니다 .
return명령문 만 허용됩니다. 생성자의 경우 초기화 목록, typedef 및 정적 어설 션 만 허용됩니다. ( = default그리고 = delete생각도 사용할 수 있습니다.)asm선언, goto명령문, case및 이외의 레이블이있는 명령문 default, try-block, 리터럴이 아닌 유형의 변수 정의, 정적 또는 스레드 스토리지 기간의 변수 정의, 초기화가 수행되지 않은 변수의 정의.위에서 말했듯 constexpr이 상수 표현식에 사용하기에 적합한 객체뿐만 아니라 객체도 선언합니다. 상수 표현식은 단순한 상수 이상의 것입니다.
템플릿 매개 변수 및 배열 크기 지정자와 같이 컴파일 타임 평가가 필요한 장소에서 사용할 수 있습니다.
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그러나 참고 :
무언가를 선언 constexpr한다고해서 컴파일시 평가 될 것이라는 보장은 없습니다. 그것은 사용할 수 있습니다 등을 위해,하지만 런타임에서 평가되는 다른 장소에서 사용뿐만 아니라 수 있습니다.
객체 는 선언 되지 않고 상수 표현식에 사용하기에 적합 할 수 있습니다 constexpr. 예:
int main()
{
const int N = 3;
int numbers[N] = {1, 2, 3}; // N is constant expression
}이것은 N선언 시간에 리터럴로 상수 및 초기화되어 선언되지 않은 경우에도 상수 표현식에 대한 기준을 충족시키기 때문에 가능합니다 constexpr.
그래서 언제 실제로 사용해야 constexpr합니까?
객체 같은 N상기 일정 식으로 사용될 수 없는 선언되고 constexpr. 다음과 같은 모든 객체에 적용됩니다.
const[이것은 §5.19 / 2로 인한 것입니다. 상수 표현식에는 "[…] 정수 또는 열거 형의 [...]"glvalue가 아닌 한 lvalue-to-rvalue 수정을 포함하는 하위 표현식이 포함되어서는 안됩니다. 이전에는 이것이 모든 리터럴 유형에 해당한다고 주장했습니다.]
상수 표현식에 사용하기 위해 함수 를 적합하게 하려면 명시 적으로 선언 해야합니다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
}때 나는 / 나는 모두를 사용한다 수 const와 constexpr 함께?
A. 객체 선언에서. 두 키워드가 선언 할 동일한 객체를 참조 할 때는 필요하지 않습니다. constexpr의미합니다 const.
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;
여전히 C ++ 14에서 const함수 로 사용할 수 있습니다.
constexpr은 일반 변수와 같이 상수가 아닌 표현식에서 함수 를 호출하면 완벽하게 합법적이며 함수는 다른 함수처럼 사용된다는 것입니다. 컴파일 타임 (평가할 수 없기 때문에)에서는 평가되지 않습니다. 아마도 당신은 그것이 명백하다고 생각할 것입니다. 그러나 선언 된 함수 constexpr가 항상 컴파일 타임에 평가 될 것이라고 말하면 잘못된 방식으로 해석 될 수 있습니다.
constexpr기능이 아닌 객체에 대해 이야기했습니다 . constexpr객체에 대해 컴파일 타임 값을 강제로 평가 constexpr하고 함수를 컴파일 타임이나 런타임에 적절하게 평가할 수 있다고 생각 합니다.
mutable의해 수정 될 수도 있습니다 const.
const적용 변수 , 그리고 수정되는 것을 방지 코드에서.
constexpr이 본 것을 컴파일러에 지시 식 A의 결과를 컴파일 시간 상수 값 이므로 배열 길이가 같은 장소에서 사용될 수를 할당하는 const등의 변수, 링크 OLI 주어진 것은 우수한 예 많이있다.
기본적으로 이들은 모두 서로 다른 두 가지 개념이며 함께 사용할 수 있습니다.
const프로그램 이 객체의 값을 변경하지 않도록 보장합니다 . 그러나 const개체가 어떤 유형의 초기화를 수행하는지 보장하지는 않습니다.
치다:
const int mx = numeric_limits<int>::max(); // OK: runtime initialization
이 함수는 max()단순히 리터럴 값을 반환합니다. 그러나 이니셜 라이저가 함수 호출이므로 mx런타임 초기화가 수행됩니다. 따라서 상수 표현식 으로 사용할 수 없습니다 .
int arr[mx]; // error: “constant expression required”constexpr매크로와 하드 코딩 된 리터럴을 만들 필요가없는 새로운 C ++ 11 키워드입니다. 또한 특정 조건에서 객체가 정적 초기화를 수행하도록 보장 합니다. 식의 평가 시간을 제어합니다. 시행함으로써 그 표현의 컴파일 타임 평가를 , constexpr당신이 진정한 정의 할 수 있습니다 상수 표현식 컴파일 시간 상수에 의존하는 코드에서, 말하기, 일반적으로 시간이 중요한 애플리케이션, 시스템 프로그래밍, 템플릿 매우 중요하고 있습니다.
일정한 표현 함수 선언 함수이다 constexpr. 본문은 비 가상적이어야하며 typedef 및 정적 어설 션을 제외하고 단일 return 문으로 만 구성되어야합니다. 인수와 반환 값은 리터럴 유형이어야합니다. 상수가 아닌 식 인수와 함께 사용할 수 있지만 완료되면 결과가 상수식이 아닙니다.
상수 표현 함수는 성능이나 형식 안전성을 저하시키지 않고 매크로 와 하드 코딩 된 리터럴 을 대체하기 위한 것입니다.
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)
상수 표현 객체가 선언 된 객체이다 constexpr. 상수 표현식 또는 상수 표현식 인수가있는 상수 표현식 생성자로 구성된 rvalue로 초기화해야합니다.
상수 표현 객체는 const사용하기 전에 초기화가 필요하고 초기화 프로그램이 상수 표현식이어야한다는 점을 제외하고 선언 된 것처럼 동작 합니다. 따라서 상수 표현식 객체는 항상 다른 상수 표현식의 일부로 사용될 수 있습니다.
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
상수 표현 생성자가 선언 된 생성자입니다 constexpr. 멤버 초기화 목록을 가질 수 있지만 typedef 및 정적 어설 션과 별도로 본문이 비어 있어야합니다. 인수에는 리터럴 유형이 있어야합니다.
상수 표현식 생성자는 생성자의 인수가 모두 상수 표현식 인 경우 컴파일러가 컴파일 타임에 객체를 초기화 할 수 있도록합니다.
struct complex
{
// constant-expression constructor
constexpr complex(double r, double i) : re(r), im(i) { } // OK: empty body
// constant-expression functions
constexpr double real() { return re; }
constexpr double imag() { return im; }
private:
double re;
double im;
};
constexpr complex COMP(0.0, 1.0); // creates a literal complex
double x = 1.0;
constexpr complex cx1(x, 0); // error: x is not a constant expression
const complex cx2(x, 1); // OK: runtime initialization
constexpr double xx = COMP.real(); // OK: compile-time initialization
constexpr double imaglval = COMP.imag(); // OK: compile-time initialization
complex cx3(2, 4.6); // OK: runtime initialization
Scott Meyers의 Effective Modern C ++ 책에 대한 팁 constexpr:
constexpr 객체는 const이며 컴파일 중에 알려진 값으로 초기화됩니다.constexpr 함수는 컴파일 중에 값을 알고있는 인수와 함께 호출 될 때 컴파일 타임 결과를 생성합니다.constexpr객체와 함수는 객체와 함수가 아닌 것보다 더 넓은 범위의 컨텍스트에서 사용될 수 있습니다 constexpr.constexpr 객체 또는 함수 인터페이스의 일부입니다.비얀 스트로브 스트 룹에 의해 "언어 4 Editon 프로그래밍은 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-statement입니다. constexpr 함수는 상수가 아닌 인수에 사용할 수 있지만 완료되면 상수 표현식이 아닙니다. 상수 표현식이 필요없는 컨텍스트에서 상수 표현식이 아닌 인수로 constexpr 함수를 호출 할 수 있으므로 상수 표현식과 변수에 대해 동일한 함수를 두 번 정의하지 않아도됩니다.
일부 장소에서는 언어 규칙 (예 : 배열 범위 (§2.2.5, §7.3), 대소 문자 레이블 (§2.2.4, §9.4.2), 일부 템플릿 인수 (§25.2)에 따라 상수 표현식이 필요합니다. constexpr을 사용하여 선언 된 상수). 다른 경우에는 컴파일 타임 평가가 성능에 중요합니다. 성능 문제와 상관없이 (불변 상태의 객체에 대한) 불변성의 개념은 중요한 설계 문제입니다 (§10.4).
모두 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.
그래서 const변수를 정의 할 수 있습니다 모두 컴파일 시간 상수 와 같은 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.
}
};
A 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 ++ 함수입니다. 그러나이 경우 비 constprpr 값을 얻습니다.
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.
constexpr int존재하지만 철자const int
A const int var는 런타임시 동적으로 값으로 설정 될 수 있으며 해당 값으로 설정되면 더 이상 변경할 수 없습니다.
A 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
}
위의 스 니펫은 잘 컴파일되며 오류가 발생하는 코드를 주석 처리했습니다.
여기서 주목할 핵심 개념은 compile time및 의 개념입니다 run time. ** know **컴파일 타임에 가능한 한 많은 것을 런타임에 성능을 향상시키기 위해 C ++에 새로운 혁신이 도입되었습니다 .
나는 어떤 대답이 실제로 부작용이 무엇인지, 실제로 무엇이 무엇인지 명확하게 밝히지 않는다고 생각합니다.
constexpr그리고 const네임 스페이스 / 파일 범위에서 리터럴 또는 표현식으로 초기화 될 때 동일합니다. 그러나 함수를 사용하면 const모든 함수로 초기화 할 수 있지만 constexpr비 constprpr (constexpr 또는 constempr이 아닌 표현식으로 표시되지 않은 함수)으로 초기화하면 컴파일러 오류가 발생합니다. 모두 constexpr와 const암시 적으로 내부 연결은 (물론 실제로, 그들은 -O1 강한 컴파일하면 링크 단계에 도착 생존하지 않으며, 변수에 있습니다 static에 대한 내부 (로컬) 링커 심볼을 방출하는 컴파일러를 강요하지 않습니다 const또는 constexpr경우에 -O1 또는 강한, 그것은이 작업을 수행하는 유일한 시간은 변수의 주소를 가지고가는 경우입니다. const와 constexpr표현하지 않는 내부 기호 것 extern즉,extern constexpr/const int i = 3;사용해야합니다). 함수에서 constexpr함수가 영구적으로 (관계없이 연결 단계에 도달하지하게 extern하거나 inline, 정의 또는 -O0 또는 -Ofast에서) 반면 const하지 않을하고, static그리고 inline단지 -O1 이상에서이 효과가 있습니다. 때 const/의 constexpr변수가에 의해 초기화되는 constexpr기능, 부하는 항상 최적화 플래그 함께 최적화되어 있지만, 기능은 경우 그것은 밖으로 최적화되지 않습니다 static또는 inline변수가없는 경우, 또는 const/ constexpr.
표준 컴파일 (-O0)
#include<iostream>
constexpr int multiply (int x, int y)
{
return x * y;
}
extern const int val = multiply(10,10);
int main () {
std::cout << val;
}
컴파일
val:
.long 100 //extra external definition supplied due to extern
main:
push rbp
mov rbp, rsp
mov esi, 100 //substituted in as an immediate
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
mov eax, 0
pop rbp
ret
__static_initialization_and_destruction_0(int, int):
.
.
.
하나
#include<iostream>
const int multiply (int x, int y)
{
return x * y;
}
const int val = multiply(10,10); //constexpr is an error
int main () {
std::cout << val;
}
컴파일
multiply(int, int):
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], edi
mov DWORD PTR [rbp-8], esi
mov eax, DWORD PTR [rbp-4]
imul eax, DWORD PTR [rbp-8]
pop rbp
ret
main:
push rbp
mov rbp, rsp
mov eax, DWORD PTR val[rip]
mov esi, eax
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
mov eax, 0
pop rbp
ret
__static_initialization_and_destruction_0(int, int):
.
.
.
mov esi, 10
mov edi, 10
call multiply(int, int)
mov DWORD PTR val[rip], eax
이것은 파일 범위 변수 constexpr의 초기화가 const/constexpr컴파일 타임에 발생하고 전역 기호를 생성하지 않는 반면,이를 사용하지 않으면 main런타임 전에 초기화가 발생 한다는 것을 분명히 보여줍니다 .
-Ofast를 사용하여 컴파일
-Ofast조차도 부하를 최적화하지 않습니다! https://godbolt.org/z/r-mhif , 그래서 당신은 필요 constexpr
constexprconstexpr동일한 결과를 위해 다른 함수 내에서 함수를 호출 할 수도 있습니다 . constexpr함수에서 함수에서 컴파일 타임에 수행 할 수없는 것을 사용하지 못하게합니다. 예를 들어,에 대한 <<운영자 호출 std::cout.
constexpr블록 범위에서 non-constexpr 함수로 초기화되면 오류가 발생한다는 점에서 동일하게 작동합니다. 값도 즉시 대체됩니다.
결국 주요 목적은 C의 인라인 함수와 비슷하지만 함수가 파일 범위 변수를 초기화하는 데 사용되는 경우에만 효과적입니다 (함수는 C에서 수행 할 수 없지만 파일의 동적 초기화를 허용하기 때문에 C ++에서 가능합니다. 범위 변수)를 제외하고는 함수가 전역 / 로컬 심볼을 링커로 내보낼 수 없으며 C 에서도 사용할 extern/static수 있습니다 inline. constexprC 및 C ++에서 -O1 최적화를 사용하여 블록 범위 변수 할당 함수를 인라인 할 수 있습니다 .
우선, 둘 다 C ++의 한정자입니다. const로 선언 된 변수는 초기화해야하며 나중에 변경할 수 없습니다. 따라서 일반적으로 const로 선언 된 변수는 컴파일하기 전에도 값을 갖습니다.
그러나 constexpr의 경우 약간 다릅니다.
constexpr의 경우 프로그램 컴파일 중에 평가할 수있는 표현식을 제공 할 수 있습니다.
분명히 constexper로 선언 된 변수는 나중에 const처럼 변경할 수 없습니다.
constexpr컴파일 타임 상수를 만듭니다.const단순히 값을 변경할 수 없음을 의미합니다.