본문 바로가기
NC University/Advanced C++

4일차 - static_assert

by 날쑤 2015. 7. 30.

Intro

  C++에서는 프로그램이 수행되는 동안 반드시 유지되어야 할 불변식(invariant)을 assert를 이용해서 검증한다. 예를 들어서 동적으로 배열을 할당할 때 배열 사이즈는 반드시 양수여야 하는데, 이를 assert를 써서 검증할 수 있다.

  기존의 assert는 유효성의 확인을 실행시간에 했었다. 그러므로 프로그램을 돌려봐야만 문제 확인이 가능했다. 하지만 C++ 11에서는 static_assert를 이용해서 컴파일 시간에 알 수 있는 조건식들에 대해서는 컴파일 타임 assertion이 가능해졌다.

static_assert

  간단한 Packet 구조체 하나를 정의하자. 그리고 Packet 타입의 사이즈를 출력해보자.

  결과값은 8로 출력된다. 이는 메모리 정렬을 맞추기 위해 컴파일러가 padding을 채우기 때문이다. 하지만 상황에 따라서 Packet의 padding이 제거되어야 할 수도 있다. 이런 경우에 컴파일러 옵션으로 padding이 제거되었는지를 static_assert로 컴파일 타임에 검증하게 할 수 있다.

  이제는 만약 pragma pack을 지우거나 혹은 컴파일러 옵션으로 padding을 제거하지 않았다면 static_assert에 걸려서 컴파일 자체가 되지 않을 것이다. 이와 같이 static_assert는 컴파일 시간에 알 수 있는 조건이 충족되지 않을 경우, 컴파일 에러를 발생시켜서 유효성 검증에 실패했음을 알려준다.

static_assert with type traits

  static_assert는 컴파일 시간 정보를 활용한다. 그러므로 타입과 관련된 유효성 검증이 필요할 때는 static_assert를 앞에서 다뤘던 type trait와 함께 사용하면 좋다. 간단한 예제를 통해 활용 예제를 살펴보자.

  두 함수 모두 특별히 의미있는 일을 하지는 않는다. 하지만 모든 타입이 아닌 특정 조건을 만족하는 타입들에 대해서만 정상적으로 동작하는 특징을 가진다. 이렇게 함수 템플릿이 인스턴스화되어 정상적으로 동작하게 하는 타입의 조건을 개념(Concept)이라고 한다. 원래 Concept은 C++ 11에 추가될 예정이었지만 결국 무산되었지만, 다가올 C++ 17에서 등장할 예정이라고 한다.

  Concept은 아직 없지만 static_assert를 이용해서 이를 대신할 수 있다. 우선 각 함수에서 필요로 하는 타입 조건을 살펴보면 foo는 T에 디폴트 생성자가 있어야하며, goo는 T가 POD(Plain Old Data)여야 한다. 이 조건들은 type trait를 이용해서 쉽게 표현할 수 있으며 이를 static_assert에 적용하면 된다.

  Point 클래스는 디폴트 생성자도 없고, POD도 아니다.(non-trivial constructor가 있으므로) 즉, Point 타입은 foo나 goo 양쪽 모두에 대해서 필요 조건(Concept)을 만족하지 못한다. 그러므로 만약 Point 객체를 foo나 goo에 적용하는 코드가 어딘가에 존재한다면, 컴파일러는 이를 컴파일 시간에 검출하고 적절한 에러 메시지를 출력해 줄 것이다.