변수에 대한 const vs constexpr


303

다음 정의 사이에 차이점이 있습니까?

const     double PI = 3.141592653589793;
constexpr double PI = 3.141592653589793;

그렇지 않다면 C ++ 11에서 어떤 스타일이 선호됩니까?



둘 다 컴파일 타임 상수입니다. 그러나 첫 번째 const_cast를 작성하고 쓸 수 있습니다. 그러나 컴파일 타임에 발생하는 "읽기"에 영향을 미치지 않기 때문에 모든 컴파일러에서 최적화됩니다.
Bonita Montero

답변:


347

차이가 있다고 생각합니다. 더 쉽게 이야기 할 수 있도록 이름을 바꾸겠습니다.

const     double PI1 = 3.141592653589793;
constexpr double PI2 = 3.141592653589793;

모두 PI1PI2당신이 그들을 수정할 수 없습니다 의미 상수입니다. 그러나 단지 PI2 컴파일 타임 상수이다. 그것은 해야 컴파일시에 초기화 될 수있다. PI1컴파일 타임 또는 런타임에 초기화 될 수 있습니다. 또한 컴파일 타임 상수가 필요한 컨텍스트 에서만 PI2 사용할 수 있습니다. 예를 들면 다음과 같습니다.

constexpr double PI3 = PI1;  // error

그러나:

constexpr double PI3 = PI2;  // ok

과:

static_assert(PI1 == 3.141592653589793, "");  // error

그러나:

static_assert(PI2 == 3.141592653589793, "");  // ok

어느 쪽을 사용해야합니까? 필요에 맞는 것을 사용하십시오. 컴파일 타임 상수가 필요한 상황에서 사용할 수있는 컴파일 타임 상수가 있는지 확인 하시겠습니까? 런타임에 수행 된 계산으로 초기화 할 수 있습니까? 기타.


60
확실해? const int N = 10; char a[N];작동 하기 때문에 배열 범위는 컴파일 타임 상수 여야합니다.
fredoverflow 16:12

10
내가 쓴 예제가 진행되는 한 확실합니다 (게시하기 전에 각각 테스트했습니다). 그러나 컴파일러는 PI1배열에서 사용하기 위해 컴파일 타임 정수 상수 로 변환 할 수 있지만 비 유형 정수 템플릿 매개 변수로는 사용할 수 없습니다. 따라서 PI1정수 형으로 의 컴파일 타임 변환 가능성은 약간 히트하고 그리워 보입니다.
Howard Hinnant

34
@FredOverflow : 비 const 배열 인덱스는 약 10 년 동안 (예 : g ++ 확장이 있음) "작동했습니다". 그러나 이것이 합법적 C ++임을 의미하지는 않습니다 (최근의 C 또는 C ++ 표준 으로 인해 합법적입니다 ) 어느 것을 잊어 버렸습니다). 컴파일 타임 상수의 차이와 관련하여 템플릿 매개 변수와 enum이니셜 라이저로 사용하는 것은 constand 사이의 유일한 두 가지 차이점 constexpr입니다 ( double어쨌든 작동하지 않습니다 ).
Damon

17
5.19 단락 4 상수 표현식 [expr.const]는 (비 규범 적) 참고 사항으로, 구현시 런타임과 컴파일 타임에 부동 소수점 산술을 다르게 (예 : 정확도와 관련하여) 다르게 수행 할 수있는 것으로 유명합니다. 그래서 1 / PI11 / PI2다른 결과를 얻을 수 있습니다. 그러나이 기술 이이 답변의 조언만큼 중요하지 않다고 생각합니다.
Luc Danton

4
그러나 그것은 constexpr double PI3 = PI1;나를 위해 올바르게 작동합니다. (MSVS2013 CTP). 내가 뭘 잘못하고 있죠?
NuPagadi

77

여기에는 차이가 없지만 생성자가있는 형식이있을 때 중요합니다.

struct S {
    constexpr S(int);
};

const S s0(0);
constexpr S s1(1);

s0상수이지만 컴파일 타임에 초기화되지는 않습니다. s1로 표시 constexpr되므로 상수이며 S의 생성자도 표시 되므로constexpr 있으므로 컴파일 타임에 초기화됩니다.

대부분 런타임에 초기화하는 데 시간이 많이 걸리고 컴파일러로 작업을 푸시하려고 할 때 중요합니다.이 작업은 시간이 많이 걸리지 만 컴파일 된 프로그램의 실행 시간을 늦추지는 않습니다.


3
나는 내가 도착한 결론 constexpr은 객체의 컴파일 타임 계산이 불가능할 경우 진단으로 이어질 것이라는 것이었다 . 덜 명확한 것은 상수 매개 변수를 기대 하는 함수 가 컴파일 타임에 실행될 수 있는지 여부는 매개 변수를 다음 const과 같이 선언 하지 말고 선언해야한다는 constexpr것입니다. 즉, constexpr int foo(S)호출하면 컴파일 타임에 실행 foo(s0)됩니까?
Matthieu M.

4
@ MatthieuM : foo(s0)컴파일 타임에 실행 될지 의심 스럽지만 알 수 없습니다. 컴파일러는 그러한 최적화를 수행 할 수 있습니다. 확실히 gcc 4.7.2 나 clang 3.2는 컴파일 할 수 없습니다constexpr a = foo(s0);
rici

50

constexpr 은 상수이며 컴파일 중에 알려진 값을 나타냅니다.
const 는 상수 인 값을 나타냅니다. 컴파일하는 동안 반드시 알아야하는 것은 아닙니다.

int sz;
constexpr auto arraySize1 = sz;    // error! sz's value unknown at compilation
std::array<int, sz> data1;         // error! same problem

constexpr auto arraySize2 = 10;    // fine, 10 is a compile-time constant
std::array<int, arraySize2> data2; // fine, arraySize2 is constexpr

const는 constexpr과 동일한 보장을 제공하지 않습니다. const 객체는 컴파일 중에 알려진 값으로 초기화 될 필요가 없기 때문입니다.

int sz;
const auto arraySize = sz;       // fine, arraySize is const copy of sz
std::array<int, arraySize> data; // error! arraySize's value unknown at compilation

모든 constexpr 객체는 const이지만 모든 constexpr 객체는 constexpr이 아닙니다.

컴파일러가 변수에 컴파일 타임 상수가 필요한 컨텍스트에서 사용할 수있는 값을 갖도록하려면, 도달 할 도구는 const가 아닌 constexpr입니다.


2
설명이 마음에 들었습니다. 실제 시나리오에서 컴파일 타임 상수를 사용해야하는 경우는 어디에 있습니까?
Mayukh Sarkar

1
@MayukhSarkar 단순히 구글 C ++ 왜 constexpr 예를 들어 stackoverflow.com/questions/4748083/…
underscore_d

18

constexpr 기호 상수는 컴파일 타임에 알려진 값을 제공해야합니다. 예를 들면 다음과 같습니다.

constexpr int max = 100; 
void use(int n)
{
    constexpr int c1 = max+7; // OK: c1 is 107
    constexpr int c2 = n+7;   // Error: we don’t know the value of c2
    // ...
}

컴파일 타임에는 알려지지 않았지만 초기화 후에도 변경되지 않는 값으로 초기화되는 "변수"의 값을 처리하기 위해 C ++은 두 번째 형태의 상수 ( const )를 제공합니다. 예를 들어 :

constexpr int max = 100; 
void use(int n)
{
    constexpr int c1 = max+7; // OK: c1 is 107
    const int c2 = n+7; // OK, but don’t try to change the value of c2
    // ...
    c2 = 7; // error: c2 is a const
}

이러한“ const 변수”는 다음 두 가지 이유로 매우 일반적입니다.

  1. C ++ 98에는 constexpr이 없었으므로 사람들은 const을 사용했습니다 .
  2. 상수 표현식은 아니지만 (컴파일 타임에 값을 알 수없는) 변수 "목록 변수"는 초기화 후에 값을 변경하지 않는 것이 그 자체로 널리 유용합니다.

참조 : Stroustrup의 "프로그래밍 : C ++을 사용한 원리 및 실습"


25
아마도 당신은 당신의 답변에있는 텍스트가 Stroustrup
Aky
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.