Namespaces
Variants

constexpr specifier (since C++11)

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

목차

설명

constexpr 지정자는 해당 개체들의 값을 컴파일 시간에 평가할 수 있음을 선언합니다. 그러한 개체들은 오직 컴파일 시간 상수 표현식 만 허용되는 곳에서 사용될 수 있습니다 (적절한 함수 인수가 제공되는 경우).

객체 선언에서 사용된 constexpr 지정자는 또는 비정적 멤버 함수 (C++14까지) const 를 암시합니다.

함수의 첫 번째 선언에서 사용된 constexpr 지정자는 또는 static 데이터 멤버 (C++17부터) inline 을 암시합니다. 함수 또는 함수 템플릿의 선언에 constexpr 지정자가 있는 경우, 모든 선언은 해당 지정자를 포함해야 합니다.

constexpr 변수

변수 또는 변수 템플릿 (C++14 이후) 는 다음 모든 조건이 충족될 경우 constexpr 로 선언될 수 있습니다:

(C++26까지)
(C++26부터)

  • 상수 파괴(constant destruction)를 가지며, 이는 다음 조건 중 하나가 충족되어야 함을 의미합니다:
  • 클래스 타입이 아니거나 (다차원 배열일 수 있는) 해당 배열이 아님.
  • constexpr 소멸자를 가지는 클래스 타입이거나 (다차원 배열일 수 있는) 해당 배열이며, 객체를 파괴하는 것만을 유일한 효과로 하는 가상 표현식 e 에 대해, 객체와 그 비가변(non-mutable) 하위 객체들의 수명이 e 내에서 시작되는 것으로 간주된다면 e 핵심 상수 표현식(core constant expression) 이 되는 경우.

constexpr 변수가 번역 단위 지역적(translation-unit-local) 이 아닌 경우, 상수 표현식에서 사용 가능한 번역 단위 지역적 개체를 참조하도록 초기화되어서는 안 되며, 그러한 개체를 참조하는 하위 객체를 가져서도 안 됩니다. 이러한 초기화는 모듈 인터페이스 단위(module interface unit) (해당 비공개 모듈 조각(private module fragment) 외부) 또는 모듈 파티션에서 허용되지 않으며, 다른 모든 상황에서는 사용이 권장되지 않습니다.

(C++20부터)

constexpr 함수

함수 또는 함수 템플릿은 constexpr 로 선언될 수 있습니다.

함수는 다음 조건을 모두 만족할 경우 constexpr-suitable 입니다:

  • 이것이 생성자인 경우 또는 소멸자인 경우 (C++20 이후) , 해당 클래스는 virtual base class 를 가지지 않습니다.
(C++20 이전)
  • 반환 타입이 존재하는 경우 literal type 입니다.
  • 모든 매개변수 타입이 literal type입니다.
(C++23 이전)
(C++20 이후)
  • 함수 본문이 = default , = delete , 또는 다음 항목들만을 포함하는 복합문인 경우:
(C++14까지)
  • 함수 본문이 = default , = delete , 또는 다음을 (C++20까지) 포함하지 않는 복합문인 경우:
(C++20까지)
  • 비 리터럴 타입 변수의 정의
  • 정적 또는 스레드 저장 기간 을 가진 변수의 정의
(C++14부터)
(C++23까지)

인스턴스화된 constexpr 함수를 제외하고, 비템플릿 constexpr 함수는 constexpr-suitable해야 합니다.

생성자가 아닌 constexpr 함수가 기본 구현되지도 않고 템플릿화되지도 않은 경우, 함수 호출이 핵심 상수 표현식 의 평가된 하위 표현식이 될 수 있는 인수 값이 존재하지 않으면, 프로그램의 형식이 잘못되었으며 진단이 필요하지 않습니다.

템플릿화된 constexpr 함수의 경우, 함수/클래스 템플릿의 어떤 특수화도 비템플릿 함수로 간주할 때 해당 템플릿 함수를 constexpr-적합하게 만들 수 없다면, 프로그램의 형식이 잘못되었으며 진단이 필요하지 않습니다.

(C++23까지)

주어진 맥락에서 constexpr 함수의 호출은 다음 예외를 제외한 모든 측면에서 동일한 맥락에서의 동등한 비- constexpr 함수 호출과 동일한 결과를 생성합니다:

constexpr 생성자

constexpr 함수의 요구사항 외에도, 생성자가 constexpr로 적합하려면 다음 모든 조건을 충족해야 합니다:

  • 해당 함수 본문이 = delete 이거나 다음 추가 요구사항을 충족하는 경우:
  • 클래스가 variant 멤버를 가진 union 인 경우, 정확히 하나의 variant 멤버가 초기화됩니다.
  • 클래스가 union-like 클래스 이지만 union이 아닌 경우, variant 멤버를 가진 각 익명 union 멤버에 대해 정확히 하나의 variant 멤버가 초기화됩니다.
  • 모든 non-variant 비정적 데이터 멤버와 기본 클래스 하위 객체가 초기화됩니다.
(C++20까지)
  • 생성자가 위임 생성자 인 경우, 대상 생성자는 constexpr 생성자입니다.
  • 생성자가 비위임 생성자인 경우, 비정적 데이터 멤버와 기본 클래스 하위 객체를 초기화하기 위해 선택된 모든 생성자는 constexpr 생성자입니다.
(C++23까지)

기본 설정되지 않거나 템플릿화되지 않은 constexpr 생성자의 경우, 함수 호출이 상수 표현식 의 대상인 일부 객체의 초기화 전체 표현식의 평가된 하위 표현식이 될 수 있는 인수 값이 존재하지 않으면, 프로그램의 형식이 잘못되었으며 진단이 필요하지 않습니다.

(C++23까지)

constexpr 소멸자

소멸자는 constexpr 로 선언할 수 없지만, trivial destructor 는 상수 표현식에서 암시적으로 호출될 수 있습니다.

(C++20 이전)

constexpr 함수의 요구사항에 더하여, 소멸자가 constexpr-suitable이 되기 위해서는 다음 모든 조건을 추가로 만족해야 합니다:

  • 클래스 타입 또는 (다차원일 수 있는) 그 배열인 모든 하위 객체에 대해, 해당 클래스 타입이 constexpr 소멸자를 가져야 합니다.
(C++23 이전)
  • 클래스가 가상 기본 클래스를 가지지 않아야 합니다.
(C++20 이후)

참고 사항

noexcept 연산자는 상수 표현식에 대해 항상 true 를 반환하므로, constexpr 함수의 특정 호출이 상수 표현식 분기를 취하는지 확인하는 데 사용할 수 있습니다:

constexpr int f(); 
constexpr bool b1 = noexcept(f()); // false, undefined constexpr function
constexpr int f() { return 0; }
constexpr bool b2 = noexcept(f()); // true, f() is a constant expression
(C++17까지)

코어 상수 표현식의 요구사항을 절대 만족시킬 수 없는 constexpr 함수를 작성하는 것이 가능합니다:

void f(int& i) // not a constexpr function
{
    i = 0;
}
constexpr void g(int& i) // well-formed since C++23
{
    f(i); // unconditionally calls f, cannot be a constant expression
}
(C++23부터)

리터럴 타입이 아닌 클래스에 대해 constexpr 생성자가 허용됩니다. 예를 들어, std::shared_ptr 의 기본 생성자는 constexpr이며, 이를 통해 상수 초기화 가 가능합니다.

참조 변수는 constexpr로 선언될 수 있습니다(이들의 초기화자는 참조 상수 표현식 이어야 함):

static constexpr int const& x = 42; // constexpr 참조를 통한 const int 객체 참조
                                    // (정적 참조에 의한 수명 연장으로 인해
                                    //  해당 객체는 정적 저장 기간을 가짐)

try 블록과 인라인 어셈블리가 constexpr 함수에서 허용되지만, 예외를 던지는 것 (catch되지 않는) (C++26부터) 또는 어셈블리 실행은 상수 표현식에서 여전히 허용되지 않습니다.

변수가 상수 소멸을 가지는 경우, 해당 소멸자가 trivial하지 않더라도 소멸자를 호출하기 위해 기계어 코드를 생성할 필요가 없습니다.

비-람다, 비-특별 멤버, 비-템플릿 constexpr 함수는 암시적으로 즉시 함수가 될 수 없습니다. 사용자는 그러한 의도된 함수 정의를 올바르게 만들기 위해 명시적으로 consteval 으로 표시해야 합니다.

(C++20부터)
기능 테스트 매크로 표준 기능
__cpp_constexpr 200704L (C++11) constexpr
201304L (C++14) 완화된 constexpr , non- const constexpr 메서드
201603L (C++17) Constexpr 람다
201907L (C++20) 단순 기본 초기화 asm 선언 in constexpr 함수
202002L (C++20) 상수 평가에서 union의 활성 멤버 변경
202110L (C++23) Non- literal 변수, 레이블 및 goto 문 in constexpr 함수
202207L (C++23) 일부 constexpr 제한 완화
202211L (C++23) static constexpr 변수 in constexpr 함수 허용
202306L (C++26) void * 에서의 Constexpr 캐스트: constexpr 타입 소거를 향하여
__cpp_constexpr_in_decltype 201711L (C++11)
(DR)
상수 평가에 필요한 경우 함수 및 변수 정의 생성
__cpp_constexpr_dynamic_alloc 201907L (C++20) constexpr 함수에서 동적 저장 기간 연산

키워드

constexpr

예제

C++11/14 constexpr 함수들을 정의하여 팩토리얼을 계산합니다; 문자열 리터럴을 확장하는 리터럴 타입을 정의합니다:

#include <iostream>
#include <stdexcept>
// C++11 constexpr 함수들은 반복 대신 재귀를 사용합니다
constexpr int factorial(int n)
{
    return n <= 1 ? 1 : (n * factorial(n - 1));
}
// C++14 constexpr 함수들은 지역 변수와 반복문을 사용할 수 있습니다
#if __cplusplus >= 201402L
constexpr int factorial_cxx14(int n)
{
    int res = 1;
    while (n > 1)
        res *= n--;
    return res;
}
#endif // C++14
// 리터럴 클래스
class conststr
{
    const char* p;
    std::size_t sz;
public:
    template<std::size_t N>
    constexpr conststr(const char(&a)[N]): p(a), sz(N - 1) {}
    // constexpr 함수들은 예외를 던져서 오류를 신호합니다
    // C++11에서는 조건 연산자 ?:에서 그렇게 해야 합니다
    constexpr char operator[](std::size_t n) const
    {
        return n < sz ? p[n] : throw std::out_of_range("");
    }
    constexpr std::size_t size() const { return sz; }
};
// C++11 constexpr 함수들은 모든 것을 단일 return 문에 넣어야 했습니다
// (C++14에는 그런 요구사항이 없습니다)
constexpr std::size_t countlower(conststr s, std::size_t n = 0,
                                             std::size_t c = 0)
{
    return n == s.size() ? c :
        'a' <= s[n] && s[n] <= 'z' ? countlower(s, n + 1, c + 1)
                                   : countlower(s, n + 1, c);
}
// 컴파일 타임 상수가 필요한 출력 함수, 테스트용
template<int n>
struct constN
{
    constN() { std::cout << n << '\n'; }
};
int main()
{
    std::cout << "4! = ";
    constN<factorial(4)> out1; // 컴파일 타임에 계산됨
    volatile int k = 8; // volatile을 사용하여 최적화 방지
    std::cout << k << "! = " << factorial(k) << '\n'; // 런타임에 계산됨
    std::cout << "\"Hello, world!\"의 소문자 개수는 ";
    constN<countlower("Hello, world!")> out2; // 암시적으로 conststr로 변환됨
    constexpr int a[12] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
    constexpr int length_a = sizeof a / sizeof(int); // C++17에서는 std::size(a),
                                                      // C++20에서는 std::ssize(a)
    std::cout << "길이 " << length_a << "인 배열의 원소들: ";
    for (int i = 0; i < length_a; ++i)
        std::cout << a[i] << ' ';
    std::cout << '\n';
}

출력:

4! = 24
8! = 40320
"Hello, world!"의 소문자 개수는 9입니다
길이가 12인 배열의 요소: 0 1 2 3 4 5 6 7 8 0 0 0

결함 보고서

다음의 동작 변경 결함 보고서들은 이전에 발표된 C++ 표준에 소급 적용되었습니다.

DR 적용 대상 게시된 동작 올바른 동작
CWG 1358 C++11 템플릿화된 constexpr 함수들도 적어도 하나의 유효한
인수 값을 가져야 했음
필요 없음
CWG 1359 C++11 constexpr 공용체 생성자들은 모든 데이터 멤버를
초기화해야 했음
비어 있지 않은 공용체의 경우 정확히 하나의
데이터 멤버만 초기화
CWG 1366 C++11 함수 본문이 = default 또는 = delete constexpr 생성자를 가진
클래스들이 가상 기본 클래스를 가질 수 있었음
해당 클래스들은 가상 기본 클래스를
가질 수 없음
CWG 1595 C++11 constexpr 위임 생성자들은 관련된 모든 생성자들이
constexpr 이어야 했음
대상 생성자만 constexpr 이면 됨
CWG 1712 C++14 constexpr 변수 템플릿은 모든 선언에 constexpr 지정자를
포함해야 했음 [1]
더 이상 요구되지 않음
CWG 1911 C++11 리터럴이 아닌 타입들에 대한 constexpr 생성자들이 허용되지 않았음 상수 초기화에서 허용됨
CWG 2004 C++11 mutable 멤버를 가진 공용체의 복사/이동이
상수 표현식에서 허용되었음
mutable 변형들이 암시적 복사/이동을
부적격하게 만듦
CWG 2022 C++98 동등한 constexpr 와 비- constexpr 함수가 동일한 결과를
생성하는지 여부가 복사 생략 수행 여부에
의존할 수 있었음
상수 표현식에서는 복사 생략이 항상
수행된다고 가정
CWG 2163 C++14 goto 문이 금지되어 있음에도 constexpr 함수에서
레이블들이 허용되었음
레이블들도 금지됨
CWG 2268 C++11 mutable 멤버를 가진 공용체의 복사/이동이
CWG 이슈 2004 의 해결에 의해 금지되었음
객체가 상수 표현식 내에서 생성된
경우 허용됨
CWG 2278 C++98 CWG 이슈 2022 의 해결이 구현 가능하지 않았음 상수 표현식에서는 복사 생략이 절대
수행되지 않는다고 가정
CWG 2531 C++11 비-인라인 변수가 constexpr 로 재선언되면
인라인이 됨
변수가 인라인이
되지 않음
  1. 이는 constexpr 지정자를 가진 변수 템플릿의 선언이 둘 이상 존재할 수 없기 때문에 중복됩니다.

참고 항목

constant expression 컴파일 타임에 평가될 수 있는 표현식 을 정의함
consteval specifier (C++20) 함수가 immediate function 임을 지정하며, 함수에 대한 모든 호출이 상수 평가에서 이루어져야 함
constinit specifier (C++20) 변수가 정적 초기화, 즉 zero initialization constant initialization 을 가짐을 단언함
C documentation for constexpr