static_assert 선언
static_assert 선언 (static_assert declaration, C++11부터)
조건이 참인지 컴파일 시점에 확인하고 싶을 때 쓰는 게 static_assert예요. 프로그램이 컴파일되는 동안 검사가 실행되어서, 조건이 거짓이면 컴파일이 실패해요. 이 페이지에서는 static_assert의 문법과, 검사가 실제로 어떻게 수행되는지 정리할게요.
출처: cppreference
본문
static_assert는 컴파일 시점 단언(assertion) 검사를 수행해요. 조건이 거짓이면 프로그램은 ill-formed가 되고, 진단 오류 메시지가 생성될 수 있어요.
문법
static_assert(bool-constexpr,unevaluated-string) |
(1) | |
static_assert(bool-constexpr) |
(2) | (C++17부터) |
static_assert(bool-constexpr,constant-expression) |
(3) | (C++26부터) |
- 고정된 오류 메시지를 갖는 static assertion.
- 오류 메시지가 없는 static assertion.
- 사용자가 생성한 오류 메시지를 갖는 static assertion. 이 문법은 문법 (1)이 매칭되지 않을 때만 매칭될 수 있어요.
동작 방식 (Explanation)
static_assert 선언은 네임스페이스와 블록 범위에 (블록 선언으로), 그리고 클래스 본문 안에 (멤버 선언으로) 나타날 수 있어요.
bool-constexpr이 잘 구성되어 true로 평가되거나, 템플릿 정의 맥락에서 평가되고 그 템플릿이 인스턴스화되지 않은 경우 이 선언은 효과가 없어요. 그렇지 않으면 컴파일 시점 오류가 발생하고, 사용자가 제공한 메시지가 있으면 그 메시지가 진단 메시지에 포함돼요.
사용자가 제공한 메시지의 텍스트는 다음과 같이 결정돼요:
- 메시지가 unevaluated-string의 문법 요구를 충족하면, 그 메시지의 텍스트는 unevaluated-string의 텍스트예요.
주의할 점 (Notes)
표준은 컴파일러가 오류 메시지의 정확한 텍스트를 출력하도록 요구하지 않아요. 다만 컴파일러는 대개 가능한 한 그렇게 해요.
| 오류 메시지는 문자열 리터럴이어야 하므로, 동적 정보나 (문자열 리터럴 그 자체가 아닌) 상수 표현식도 담을 수 없어요. 특히 템플릿 타입 인자의 이름을 담을 수 없어요. | (C++26 이전) |
|---|
키워드
static_assert
예제
여기서는 static_assert가 실제로 어떤 시점에 무엇을 검사하는지 확인해 볼게요. 특히 템플릿 안에서의 동작이 관건이에요.
#include <format>
#include <type_traits>
static_assert(03301 == 1729); // C++17부터 메시지 문자열은 선택 사항
template<class T>
void swap(T& a, T& b) noexcept
{
static_assert(std::is_copy_constructible_v<T>,
"Swap requires copying");
static_assert(std::is_nothrow_copy_constructible_v<T> &&
std::is_nothrow_copy_assignable_v<T>,
"Swap requires nothrow copy/assign");
auto c = b;
b = a;
a = c;
}
template<class T>
struct data_structure
{
static_assert(std::is_default_constructible_v<T>,
"Data structure requires default-constructible elements");
};
template<class>
constexpr bool dependent_false = false; // CWG2518/P2593R1 이전의 우회책
template<class T>
struct bad_type
{
static_assert(dependent_false<T>,
"error on instantiation, workaround");
static_assert(false,
"error on instantiation"); // CWG2518/P2593R1 덕분에 OK
};
struct no_copy
{
no_copy(const no_copy&) = delete;
no_copy() = default;
};
struct no_default
{
no_default() = delete;
};
#if __cpp_static_assert >= 202306L // 아직 실제 C++은 아님 (std::format이 constexpr이어야 동작):
static_assert(sizeof(int) == 4,
std::format("Expected 4, got {}", sizeof(int)));
#endif
int main()
{
int a, b;
swap(a, b);
no_copy nc_a, nc_b;
swap(nc_a, nc_b); // 1
[[maybe_unused]] data_structure<int> ds_ok;
[[maybe_unused]] data_structure<no_default> ds_error; // 2
}
가능한 출력:
1: error: static assertion failed: Swap requires copying
2: error: static assertion failed: Data structure requires default-constructible elements
3: error: static assertion failed: Expected 4, got 2
먼저 static_assert(03301 == 1729)는 C++17부터 메시지 인자를 생략할 수 있다는 걸 보여줘요. swap이 no_copy와 함께 쓰이면 복사 생성이 불가능하므로 1번 static_assert가 실패해요. 그리고 data_structure<no_default>는 기본 생성이 불가능한 타입이라 2번에서 실패하고요. 이처럼 컴파일 타임에 타입 특성을 점검할 수 있게 해주는 게 static_assert예요.
더 알아보기
#error지시문: 주어진 오류 메시지를 표시하고 프로그램을 ill-formed로 만든다 (전처리 지시문).assert: 사용자가 지정한 조건이 참이 아니면 프로그램을 중단한다 (함수 매크로).contract_assert문(C++26): 실행 중에 내부 조건을 검증한다.- Type traits(C++11): 타입의 속성을 조회하는 컴파일 타임 템플릿 기반 인터페이스를 정의한다.