constexpr

Modern C++에 적응하기
Effective Modern C++ Chapter 3. constexpr


constexpr

constexpr은 해당 값이 const일 뿐만 아니라, 컴파일 타임 에 평가될 수 있음을 의미한다.

constexpr로 선언된 객체는 const이며, 그 값은 컴파일 타임에 정해진다. 따라서 컴파일 타임 이후에 평가되는 값으로 초기화할 수 없다.

1
2
3
4
5
int sz;
...

constexpr auto arrSize1 = sz; // error
constexpr auto arrSize2 = 10; // OK

컴파일 타임에 정해진 상수이므로 배열의 길이나 템플릿 인수 등, 정수 상수 표현식 에 사용할 수 있다.


constexpr로 선언된 함수는 함수에 넘겨지는 인수에따라 실행시점이 나뉜다.

  • 인수가 컴파일 타임에 평가될 수 있다면, 함수의 결과 또한 컴파일 타임 상수로써 평가된다.
  • 인수중 하나라도 컴파일 타임에 평가될 수 없다면, 보통의 함수처럼 런타임에 평가된다.
1
2
3
4
5
6
constexpr int pow(int base, int exp) noexcept
{
...
}

std::array<int, pow(3, 5)> results; // OK

만약 constexpr함수의 구현이 절대 컴파일 타임에 평가될 수 없다면 컴파일 에러가 발생한다.

1
2
3
4
5
6
constexpr int pow(int base, int exp) noexcept
{
return static_cast<int>(std::pow(base, exp)); // error
}

...

std::powconstexpr로 선언된 함수가 아니다.(즉, 런타임에 평가된다.) 따라서 위 함수는 절대 컴파일타임에 평가될 수 없고, 컴파일 에러가 발생한다.


C++11 에서는 constexpr함수를 정의할 때 지역변수를 사용할 수 없고, 하나의 반환 표현식만 사용해야 했다.[1]

C++11
1
2
3
4
constexpr int pow(int base, int exp) noexcept
{
return (exp == 0 ? 1 : base * pow(base, exp - 1));
}

C++14 부터는 이런 제약이 사라져 보다 자유로운 구현이 가능하다.

C++14
1
2
3
4
5
6
7
8
constexpr int pow(int base, int exp) noexcept
{
auto result = 1;
for(int i = 0; i < exp; ++i)
result *= base;

return result;
}

constexpr의 반환 타입은 Literal Type 이어야 한다. 이는 컴파일 타임에 그 값을 알 수 있는 타입인데, C++11 에서는 void를 제외한 기본 내장 타입이 여기에 해당한다.
생성자가 constexpr이고, 멤버 변수도 컴파일 타임에 평가가 가능한 사용자 정의 타입 역시 리터럴에 속한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Point {
public:
constexpr Point(double x = 0.0, double y = 0.0) noexcept :
x(x), y(y) {}

constexpr double GetX() const noexcept { return x; }
constexpr double GetY() const noexcept { return y; }

private:
double x, y;
};

constexpr Point p1(9.4, 27.7);
constexpr auto p1X = p1.GetX();
constexpr auto p1Y = p1.GetY();

C++14 부터는 voidLiteral Type 에 포함되었고, C++11에서의 constexpr함수는 암묵적으로 const라는 제약이 사자렸기 때문에 setter 함수역시 constexpr로 선언 가능하다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Point {
public:
...
constexpr void SetX(double x) noexcept { this->x = x; }
constexpr void SetY(double Y) noexcept { this->y = y; }

private:
double x, y;
};

constexpr Point Reflect(const Point& p)
{
Point result;
result.SetX(-p.GetX());
result.SetY(-p.GetY());

return result;
}


References


  1. ‘반환’ 표현식인 이유는 constexpr함수의 반환 타입은 Literal Type 이어야한다는 제약 때문이다. C++11에서 voidLiteral Type이 아니었고, C++14부터 포함되었다. ↩︎

Author

Joyus.Gim

Posted on

2022-07-26

Updated on

2022-07-26

Licensed under

Comments