[C++] constexpr
Updated:
개요
- ‘상수(const) + 컴파일 시점에 값 평가 가능’함을 선언
- 상수 표현식에 사용 가능
- constexpr로 선언되는 변수 혹은 함수의 리턴 타입은 리터럴 타입이여야 함
- constexpr 변수
- const와는 달리 컴파일 타임에 초기화가 가능해야 함
- constexpr 함수
- constexpr 조건을 만족하면 constexpr, 아니면 일반 함수로 동작
- constexpr 인자로 호출 되면 컴파일 시점에 수행
- 런타임에 정해지는 인자로 호출 되면 런타임에 수행
- 암시적으로 inline 함수
- 람다의 경우 C++17부터 constexpr를 지원
- constexpr를 지정하지 않으면 constexpr 조건이 만족하는 경우에만 constexpr로 동작
- 제약사항
- C++11
- 실행 가능 문장이 하나 이하여야 함(보통은 return문)
- if문 대신 삼항 연산자, 루프 대신 재귀를 이용하여 회피 가능
- constexpr 함수를 암묵적으로 const로 변경하므로 set 함수에 선언 불가
- C++14
- 로컬 변수, 루프, n개의 return문 허용
- set 함수 선언 가능
- C++11
- TMP 보다 직관적인 코드 작성 가능
- constexpr 객체
- 컴파일 시점에 값 평가 가능한 값으로 초기화
- constexpr if
- C++17부터 지원
- 상수 표현식의 boolean 여부를 컴파일 타임에 평가
- 조건이 true면 false 코드, 조건이 false면 true 코드가 컴파일 대상에서 제외
- #if문 등 보다 직관적인 코드 작성 가능
- 가능한 한 constexpr 사용하는 것이 자연스러운 성능 향상 및 안정성에 도움
예제
- 코드
#include <iostream> using namespace std; template <int I> struct print { print() { cout << I << endl; } }; void test_variable() { cout << "--- " << __func__ << " start ---" << endl; constexpr int ii1 = 1; print<ii1>(); constexpr int i2 = 1; constexpr int ii2 = i2; print<ii2>(); const int i3 = 1; constexpr int ii3 = i3; print<ii3>(); /* compile error int i4 = 1; constexpr int ii4 = i4; const int i5 = i; constexpr int ii5 = i5; constexpr int ii6; */ cout << "--- " << __func__ << " end ---" << endl; } void test_function() { cout << "--- " << __func__ << " start ---" << endl; auto get = [](int i) { return i; }; constexpr int ii1 = get(1); print<ii1>(); /* compile error int i2 = 1; constexpr int ii2 = get(i2); print<ii2>(); int i3 = 1; const int i33 = i3; constexpr int ii3 = get(i33); print<ii3>(); */ const int i4 = 1; constexpr int ii4 = get(i4); print<ii4>(); constexpr int i5 = 1; constexpr int ii5 = get(i5); print<ii5>(); cout << "--- " << __func__ << " end ---" << endl; } void test_class() { cout << "--- " << __func__ << " start ---" << endl; class Test { private: int i; public: constexpr Test(int i) : i(i){}; ~Test() = default; constexpr int GetI() const { return this->i; }; constexpr void SetI(int i) { this->i = i; }; }; constexpr Test t1(1); print<t1.GetI()>(); int i2 = 1; Test t2(i2); // compile error // print<t2.GetI()>(); /* compile error int i3 = 1; constexpr Test t3(i3); */ cout << "--- " << __func__ << " end ---" << endl; } template <int N> struct FactorialTmp { enum { value = N * FactorialTmp<N - 1>::value }; }; template <> struct FactorialTmp<0> { enum { value = 1 }; }; constexpr int factorial_constexpr(int n) { return n >= 0 ? n * factorial_constexpr(n - 1) : 1; } void test_compare_tmp() { cout << "--- " << __func__ << " start ---" << endl; enum FACTORIAL { first = FactorialTmp<1>::value, second = factorial_constexpr(2), third = factorial_constexpr(3), }; cout << FACTORIAL::first << ", " << FACTORIAL::second << ", " << FACTORIAL::third << endl; cout << "--- " << __func__ << " end ---" << endl; } void test_if() { cout << "--- " << __func__ << " start ---" << endl; constexpr int i = 1; if constexpr (i == 1) { cout << "if" << endl; } else { cout << "else" << endl; } cout << "--- " << __func__ << " end ---" << endl; } int main() { test_variable(); cout << endl; test_function(); cout << endl; test_class(); cout << endl; test_compare_tmp(); cout << endl; test_if(); return 0; }
- 실행 결과
--- test_variable start --- 1 1 1 --- test_variable end --- --- test_function start --- 1 1 1 --- test_function end --- --- test_class start --- 1 --- test_class end --- --- test_compare_tmp start --- 1, 0, 0 --- test_compare_tmp end --- --- test_if start --- if --- test_if end ---