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