1편, 2편, 3편, 4편에서 이어집니다.

문법 정리

constexpr

c++11에 새롭게 도입되었고 점차 발전되고 있는 키워드이다.

쉽게 말하자면 컴파일 시간에 정의될 수 있는 상수를 연산을 통해 정의할 수 있다고 생각할 수 있다.

변수와 함수 모두에 사용할 수 있다.

이는 템플릿 메타프로그램에도 잘 어울린다.

const ll mod = 1e9 + 7;  
constexpr inline ll md(ll m, ll x) { return x >= 0 && x < m ? x : (x %= m) < 0 ? x + m : x; }  
constexpr inline ll md(ll x) { return md(mod, x); }  
  
constexpr ll Factorial(int n) {  
   ll total = 1;  
   for (int i = 1; i <= n; i++) {  
      total = md(total * i);  
   }  
   return total;  
}  
  
template<int N>  
struct A {  
   int operator()() { return N; }  
};  
  
int main() {  
   fastio;  
   A<Factorial(100000)> a;  
  
   std::cout << a() << endl;  
}

constexpr 함수의 제약

  • goto
  • 리터럴 타입이 아닌 변수 정의
  • 초기화 되지 않은 변수 정의
  • 실행 중간에 constexpr이 아닌 함수 호출

이 행위들은 불가능하다.

constexpr 생성자

생성자를 constexpr을 붙여서 정의할 수 있고 위에서의 제약을 모두 따른다.

또한, 해당 클래스는 다른 클래스를 virtual 상속받을 수 없다.

class A {  
public:  
   int a, b;  
   constexpr A(int a, int b) : a(a), b(b) {}  
};  
  
int main() {  
   constexpr A a{1, 2};  
   cout << a.a;  
   return 0;  
}

if constexpr

템플릿 타입을 constexpr로 검사할 수 있는 아름다운 방법이 있다.

이제 type_traits 헤더의 것들에서 TMP를 써서 만들어진 유틸리티 함수들을 사용할 수 있다. is_integral, is_pointer 등 여러가지가 있다.

template<typename T>  
void fn(T a) {  
   if constexpr (std::is_integral<T>::value) {  
      cout << "integer\n";  
   } else if constexpr (std::is_pointer<T>::value) {  
      cout << "pointer \n";  
   } else {  
      cout << "not integer\n";  
   }  
}  
  
int main() {  
   fn(1);  
   fn(1.1);  
   int *a = new int(5);  
   fn(a);  
   return 0;  
}
integer
not integer
pointer 

is_integral<T>::value 처럼 해야하는 것을 c++17에 등장한 value를 바로 가져와주는 is_integral_v<T>를 쓸 수도 있다.

하지만 constexpr을 붙이지 않아도 위 코드는 런타임에라도 타입 검사를 할 수는 있는데, constexpr을 붙이면 저 안에서 항상 저 타입이란게 컴파일 타임에 보장이 되어서 관련된 연산을 할 수 있거나 사용성이 높아진다.

c++20

std::vector, std::string이나 여러 stl의 알고리즘들도 constexpr이 가능해졌다.

const vs constexpr

const는 값이 불변이라는 것을 나타내고 컴파일 타임에 상수여야 할 필요는 없지만 constexpr는 항상 컴파일 타임에 상수여야한다.

Categories:

Updated:

Comments