3 분 소요

개요

  • ‘상수(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 ---