Updated:

3 minute read

개요

  • ‘상수(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 함수 선언 가능
    • 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 ---