C++ 아이콘 제작자: Darius Dan - Flaticon
- 컴파일 타임에 어떠한 식의 값을 결정할 수 있다면 해당 식을 상수식 (Constant expression) 이라고 표현
- 상수식들 중에서 값이 정수인 것을 정수 상수식 (Integral constant expression)
배열 선언식 arr[size] 가 컴파일 되기 위해선 size가 정수 상수식이여야 하고
템플릿 타입 인자도 정수 상수식, enum에서 값을 지정할 때도 정수 상수식이여야 한다.
C++ 언어상 정수 상수식이 등장하는 곳은 매우 많다.
int a;
const int b = a;
b는 컴파일 타임에 값을 알 수 없지만 값을 지정해주면 값을 바꿀 수 없다는 것은 확실하다.
만약 상수식으로 초기화 한다면 컴파일러 마음대로 어느 타임에 초기화가 가능하다.
그래서 정확히 컴파일 타임 상수를 얻고자 하면 constexpr를 사용하자.
int a;
constexpr int b = a; // 잘못됨
constexpr 변수는 반드시 오른쪽에 다른 상수식이 와야한다.
하지만 위의 예제는 컴파일러 입장에서 컴파임타임에 a가 머일지 모른다.
==> 컴파일 오류
하지만 구현된 코드를 이해하기 어렵고
반복문들은 재귀 호출의 형태로 구현해야 해서 복잡하다.
constexpr int Factorial(int n) {
int total = 1;
for (int i = 1; i <= n; i++) { total *= i; }
return total;
}
template <int N>
struct A {
int operator()() { return N; }
};
int main() {
A<Factorial(10)> a; // Factorial(10) 이 컴파일 타임에 계산
std::cout << a() << std::endl;
}
- goto 문 사용
- 예외 처리 (try 문; C++ 20 부터 가능하게 바뀌었습니다.)
- 리터럴 타입이 아닌 변수의 정의
- 초기화 되지 않는 변수의 정의
- 실행 중간에 constexpr 이 아닌 함수를 호출하게 됨
- void 형
- 스칼라 타입 (char, int, bool, long, float, double) 등등
- 레퍼런스 타입
- 리터럴 타입의 배열
- 디폴트 소멸자를 가지고 다음 중 하나를 만족하는 타입
- 람다 함수
- Arggregate 타입 (사용자 정의 생성자, 소멸자가 없으며 모든 데이터 멤버들이 public) 쉽게 말해 pair 같은 애들을 이야기함
- constexpr 생성자를 가지며 복사 및 이동 생성자가 없음
constexpr 덕분에 사용자가 직접 리터럴 타입을 만들 수 있게 되었다
constexpr 생성자는 인자들이 반드시 리터럴 타입이여야 하고 해당 클래스는 다른 클래스를 상속 받을 수 없다.
class Vector {
public:
constexpr Vector(int x, int y) : x_(x), y_(y) {} // 리터럴 타입을 인자로 받음
constexpr int x() const { return x_; }
constexpr int y() const { return y_; }
private:
int x_;
int y_;
};
constexpr Vector Add(const Vector& v1, const Vector& v2) { return { v1.x() + v2.x(), v1.y() + v2.y() }; }
template <int N>
struct A { int operator()() { return N; } };
int main() {
constexpr Vector v1{ 1, 2 };
constexpr Vector v2{ 2, 3 };
A<v1.x()> a; // constexpr 객체의 constexpr 멤버 함수는 역시 constexpr!
std::cout << a() << std::endl;
A<Add(v1, v2).x()> b; // AddVec 역시 constexpr 을 리턴한다.
std::cout << b() << std::endl;
}
- 두 멤버 변수를 접근하는 함수 역시 constexpr로 정의했다.
==> 두 멤버 변수를 접근하는 함수 역시 constexpr로 정의- constexpr 생성자를 만들어서 해당 클래스 객체 생성을 constexpr로 만들 수 있다.
==> 해당 생성자가 없으면 못 만듦
A<v1.x()> a;
그리고 v1의 constexpr 멤버 함수인 x를 호출하였는데
x 역시 constexpr 함수이므로 위 코드는 결국 A<1> a 와 다름이 없다
만일 v1 이나 x 가 하나라도 constexpr 이 아니라면 위 코드는 컴파일 오류 발생
어떠한 타입인지 검사를 해서 해당 타입에 대해서만 어떠한 작업을 하고 아니라면 일반적인 작업을 하는 로직을 짜고 싶을 때 이용한다.
개인 공부 기록용 블로그입니다.
틀린 부분 있으다면 지적해주시면 감사하겠습니다!!