`constexpr`과`const`의 차이점


593

차이 무엇 constexprconst?

  • 언제 하나만 사용할 수 있습니까?
  • 둘 다 언제 사용할 수 있으며 어떻게 선택해야합니까?

71
constexpr컴파일 타임 상수를 만듭니다. const단순히 값을 변경할 수 없음을 의미합니다.
0x499602D2


boost/hana라이브러리 에서이 기사를 constexpr사용 constexpr하여 사용할 수없고 사용할 수없는 일부 문제점을 밝힐 수 있습니다 . boost.org/doc/libs/1_69_0/libs/hana/doc/html/…
Andry

0x499602D2 @ " 단순히 그 값이 변경 될 수 없음을 의미 하는 문자로 초기화 스칼라 위해 변경 될 수없는 값" 이며 또한 컴파일 시간 상수.
curiousguy

@ curiousguy 그래 내 의견은 너무 단순화되었습니다. 틀림없이 나는 constexpr
그때도 처음이었다

답변:


587

기본 의미와 구문

두 키워드는 함수뿐만 아니라 객체 선언에도 사용할 수 있습니다. 객체에 적용 할 때의 기본적인 차이점 은 다음과 같습니다.

  • const객체를 constant 로 선언합니다 . 이는 일단 초기화되면 해당 객체의 값이 변경되지 않으며 컴파일러가이 사실을 최적화에 활용할 수 있음을 보증합니다. 또한 프로그래머가 초기화 후에 수정되지 않은 객체를 수정하는 코드를 작성하지 못하게합니다.

  • constexprStandard가 상수 표현식을 호출하는 데 사용하기에 적합한 객체를 선언합니다 . 그러나 constexpr이것이 유일한 방법은 아닙니다.

함수에 적용 할 때 기본적인 차이점은 다음과 같습니다.

  • const비 정적 멤버 함수에만 사용할 수 있으며 일반적인 함수는 사용할 수 없습니다. 멤버 함수가 비 정적 데이터 멤버를 수정하지 않음을 보증합니다.

  • constexpr멤버 함수와 비 멤버 함수 및 생성자 모두와 함께 사용할 수 있습니다. 상수 표현식 에 사용하기에 적합한 함수를 선언합니다 . 컴파일러는 함수가 특정 기준 (7.1.5 / 3,4), 가장 중요하게 (†)를 충족하는 경우에만 수락합니다 .

    • 함수 본문은 비 가상적이며 매우 간단해야합니다. typedef 및 정적 어설 션 외에는 단일 return명령문 만 허용됩니다. 생성자의 경우 초기화 목록, typedef 및 정적 어설 션 만 허용됩니다. ( = default그리고 = delete생각도 사용할 수 있습니다.)
    • C ++ 14부터 규칙이 완화되어 constexpr 함수 내부에서 허용되는 것 : 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
    }

때 나는 / 나는 모두를 사용한다 수 constconstexpr 함께?

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주소 상수 표현, 즉 그 자체가 상수 표현 인 포인터로 선언됩니다. 여기서 (. 어드레스가 정적 / 전역 상수 식으로 어드레스 오퍼레이터를 적용시킴으로써 생성되는 경우가있다), 양 constexprconst요구된다 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함수 로 사용할 수 있습니다.


3
"컴파일 타임에 반드시 평가 될 필요는 없다"는 IMO는 "컴파일 타임에 평가 된"것으로 생각하는 것보다 덜 유용하다. 상수 표현식에 대한 제약 조건은 컴파일러가이를 쉽게 평가할 수 있음을 의미합니다. 제약 조건이 충족되지 않으면 컴파일러에서 불만을 제기해야합니다. 부작용이 없으므로 컴파일러가 "평가"했는지 여부를 절대로 구별 할 수 없습니다.
aschepler

10
@aschepler 물론입니다. 내 주요 요점 constexpr은 일반 변수와 같이 상수가 아닌 표현식에서 함수 를 호출하면 완벽하게 합법적이며 함수는 다른 함수처럼 사용된다는 것입니다. 컴파일 타임 (평가할 수 없기 때문에)에서는 평가되지 않습니다. 아마도 당신은 그것이 명백하다고 생각할 것입니다. 그러나 선언 된 함수 constexpr가 항상 컴파일 타임에 평가 될 것이라고 말하면 잘못된 방식으로 해석 될 수 있습니다.
jogojapan

5
예, constexpr기능이 아닌 객체에 대해 이야기했습니다 . constexpr객체에 대해 컴파일 타임 값을 강제로 평가 constexpr하고 함수를 컴파일 타임이나 런타임에 적절하게 평가할 수 있다고 생각 합니다.
aschepler

2
수정 : 'const'는 변수 값을 변경할 수없는 제한 사항입니다. 가치가 변하지 않을 것이라는 약속은하지 않는다 (즉, 다른 사람에 의해). 읽기 속성이 아니라 쓰기 속성입니다.
Jared Grubb

3
이 문장 은 멤버 함수가 비 정적 데이터 멤버를 수정하지 않음을 보증합니다. 한 가지 중요한 세부 사항이 누락되었습니다. 로 표시된 멤버는 멤버 기능에 mutable의해 수정 될 수도 있습니다 const.
Omnifarious

119

const적용 변수 , 그리고 수정되는 것을 방지 코드에서.

constexpr이 본 것을 컴파일러에 지시 A의 결과를 컴파일 시간 상수 값 이므로 배열 길이가 같은 장소에서 사용될 수를 할당하는 const등의 변수, 링크 OLI 주어진 것은 우수한 예 많이있다.

기본적으로 이들은 모두 서로 다른 두 가지 개념이며 함께 사용할 수 있습니다.



64

개요

  • 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 객체 또는 함수 인터페이스의 일부입니다.

출처 : constexpr 사용은 C의 보안, 성능 및 캡슐화를 ++ 개선하기 위해 .


다른 상황을 보여주는 훌륭한 예제 코드에 감사드립니다. 다른 설명 중 일부와 마찬가지로, 실제로 작동하는 코드를 보는 것이 훨씬 유용하고 이해 가능하다는 것을 알았습니다. 실제로 무슨 일이 일어나고 있는지에 대한 이해를 굳건히하는 데 도움이되었습니다.
RTHarston

35

비얀 스트로브 스트 룹에 의해 "언어 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.4square(var); // error : var is not a constant expression
const double max3 = 1.4square(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 xx; }


constexpr이 되려면 함수는 단순해야합니다. 값을 계산하는 return-statement입니다. constexpr 함수는 상수가 아닌 인수에 사용할 수 있지만 완료되면 상수 표현식이 아닙니다. 상수 표현식이 필요없는 컨텍스트에서 상수 표현식이 아닌 인수로 constexpr 함수를 호출 할 수 있으므로 상수 표현식과 변수에 대해 동일한 함수를 두 번 정의하지 않아도됩니다.
일부 장소에서는 언어 규칙 (예 : 배열 범위 (§2.2.5, §7.3), 대소 문자 레이블 (§2.2.4, §9.4.2), 일부 템플릿 인수 (§25.2)에 따라 상수 표현식이 필요합니다. constexpr을 사용하여 선언 된 상수). 다른 경우에는 컴파일 타임 평가가 성능에 중요합니다. 성능 문제와 상관없이 (불변 상태의 객체에 대한) 불변성의 개념은 중요한 설계 문제입니다 (§10.4).


여전히 성능 문제가 있습니다. 런타임에 평가되는 경우 constexpr 함수가 비 constprpr 버전의 함수보다 느릴 수 있습니다. 또한 상수 값이 있다면 "const"또는 "constexpr"을 선호해야합니까? (더 많은 스타일 질문 생성 어셈블리는 동일하게 보입니다)
CoffeDeveloper

31

모두 constconstexpr변수와 함수에 적용 할 수 있습니다. 그것들은 서로 비슷하지만 실제로는 매우 다른 개념입니다.

모두 constconstexpr평균 그 값은 초기화 후 변경할 수 없습니다. 예를 들어 :

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.

간의 주된 차이 constconstexpr그들의 초기 값이 공지 된 시간 (평가). 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항상 배열 크기를 지정할 수있는 컴파일 시간 상수를 정의하십시오.

모두 constconstexpr너무 기능에 적용 할 수 있습니다. 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.

또한, C에 constexpr int존재하지만 철자const int
curiousguy

8

@ 0x499602d2가 이미 지적했듯이 const초기화 후 constexpr(C ++ 11에서 도입 됨) 변수가 컴파일 시간 상수 임을 보장하는 값을 변경할 수 없도록합니다 .
LearnCpp.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

5

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 ++에 새로운 혁신이 도입되었습니다 .


3

나는 어떤 대답이 실제로 부작용이 무엇인지, 실제로 무엇이 무엇인지 명확하게 밝히지 않는다고 생각합니다.

constexpr그리고 const네임 스페이스 / 파일 범위에서 리터럴 또는 표현식으로 초기화 될 때 동일합니다. 그러나 함수를 사용하면 const모든 함수로 초기화 할 수 있지만 constexpr비 constprpr (constexpr 또는 constempr이 아닌 표현식으로 표시되지 않은 함수)으로 초기화하면 컴파일러 오류가 발생합니다. 모두 constexprconst암시 적으로 내부 연결은 (물론 실제로, 그들은 -O1 강한 컴파일하면 링크 단계에 도착 생존하지 않으며, 변수에 있습니다 static에 대한 내부 (로컬) 링커 심볼을 방출하는 컴파일러를 강요하지 않습니다 const또는 constexpr경우에 -O1 또는 강한, 그것은이 작업을 수행하는 유일한 시간은 변수의 주소를 가지고가는 경우입니다. constconstexpr표현하지 않는 내부 기호 것 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 최적화를 사용하여 블록 범위 변수 할당 함수를 인라인 할 수 있습니다 .


링커에 대한 좋은 지적. constexpr을 사용하면 심볼 누출이 줄어들 기 때문에 일반적으로 더 안전한 것으로 간주 될 수 있습니까?
닐 맥길

1
@NeilMcGill은 인라인 및 정적으로 인해 컴파일러가 -O1 이상을 사용하여 컴파일하는 경우 곱하기 위해 로컬 기호를 방출하지 않기 때문에 실제로는 아닙니다. Constexpr은 val의로드를 최적화하는 유일한 방법이지만 함수 앞에 정적 또는 인라인을 넣는 것과 동일합니다. 나는 또 다른 것을 잊었다. Constexpr은 -O0, 정적 및 인라인에서 함수에 대한 기호를 표시하지 않는 유일한 키워드입니다.
Lewis Kelsey

1

우선, 둘 다 C ++의 한정자입니다. const로 선언 된 변수는 초기화해야하며 나중에 변경할 수 없습니다. 따라서 일반적으로 const로 선언 된 변수는 컴파일하기 전에도 값을 갖습니다.

그러나 constexpr의 경우 약간 다릅니다.

constexpr의 경우 프로그램 컴파일 중에 평가할 수있는 표현식을 제공 할 수 있습니다.

분명히 constexper로 선언 된 변수는 나중에 const처럼 변경할 수 없습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.