static_assert는 무엇을하며 무엇을 위해 사용합니까?


117

static_assert(...)( 'C ++ 11')이 문제를 우아하게 해결할 수있는 예를 들어 주 시겠습니까?

나는 런타임에 익숙합니다 assert(...). 언제 static_assert(...)일반보다 선호해야 assert(...)합니까?

또한 boost라는 BOOST_STATIC_ASSERT것이 있습니다 static_assert(...).


추가 옵션은 BOOST_MPL_ASSERT, BOOST_MPL_ASSERT_NOT, BOOST_MPL_ASSERT_MSG, BOOST_MPL_ASSERT_RELATION [ boost.org/doc/libs/1_40_0/libs/mpl/doc/refmanual/asserts.html]도 참조하세요. _MSG는 사용 방법을 알아 내면 특히 좋습니다.
KitsuneYMG

답변:


82

내 머릿속에서 ...

#include "SomeLibrary.h"

static_assert(SomeLibrary::Version > 2, 
         "Old versions of SomeLibrary are missing the foo functionality.  Cannot proceed!");

class UsingSomeLibrary {
   // ...
};

이것이 d SomeLibrary::Version가 아닌 정적 const로 선언 되었다고 가정합니다 #define(C ++ 라이브러리에서 예상하는 것처럼).

실제로 컴파일 할 필요와 명암 SomeLibrary만을 실행하고 코드 링크 모든 것을 실행 한 후 당신이 호환되지 않는 버전을 컴파일 30 분 소요 알아 SomeLibrary.

@Arak, 귀하의 의견에 대한 응답으로 : 예, 당신은 static_assert그 모습에서 어디에서나 앉아 있을 수 있습니다 .

class Foo
{
    public: 
        static const int bar = 3;
};

static_assert(Foo::bar > 4, "Foo::bar is too small :(");

int main()
{ 
    return Foo::bar;
}
$ g ++ --std = c ++ 0x a.cpp
a.cpp : 7 : 오류 : 정적 어설 션 실패 : "Foo :: bar가 너무 작습니다 :("

1
약간 혼란 스럽 static_assert습니다. 비 실행 컨텍스트에 넣을 수 있습니까? 아주 좋은 예인 것 같습니다. :)
AraK

3
예, 정적 인 어설 션은 일반적으로 조건자가 참인 경우에만 정의되는 객체를 생성하는 것으로 구현됩니다. 이것은 단지 글로벌을 만들 것입니다.
GManNickG

이것이 원래의 질문에 대한 전체적인 답이 될 수 있을지 모르겠지만 멋진 데모
Matt Joiner

2
이 답변은 assert from <cassert>와 static_assert
bitek

11
@monocoder : "대비 ..."로 시작하는 단락을 참조하십시오. 간단히 말해서 assert 는 런타임에 조건을 확인 하고 static_assert 는 컴파일시 조건을 확인합니다. 따라서 주장하는 조건이 컴파일 타임에 알려진 경우 static_assert. 프로그램이 실행될 때까지 조건을 알 수없는 경우 assert.
Mike DeSimone 2013 년

131

정적 어설 션은 컴파일 타임에 어설 션을 만드는 데 사용됩니다. 정적 어설 션이 실패하면 프로그램은 단순히 컴파일되지 않습니다. 이는 예를 들어 unsigned int정확히 32 비트를 가진 객체에 크게 의존하는 코드로 일부 기능을 구현하는 경우와 같은 다양한 상황에서 유용합니다 . 다음과 같이 정적 주장을 넣을 수 있습니다.

static_assert(sizeof(unsigned int) * CHAR_BIT == 32);

귀하의 코드에서. 다른 플랫폼에서는 크기가 다른 unsigned int유형의 컴파일이 실패하므로 개발자가 코드의 문제 부분에주의를 기울이고 다시 구현하거나 다시 검사하도록 조언합니다.

다른 예를 들어, void *함수에 대한 포인터로 정수 값을 전달하고 싶을 수 있습니다 (핵이지만 때때로 유용함). 정수 값이 포인터에 맞는지 확인하고 싶을 수 있습니다.

int i;

static_assert(sizeof(void *) >= sizeof i);
foo((void *) i);

char유형이 서명 된 자산을 자산화 할 수 있습니다.

static_assert(CHAR_MIN < 0);

또는 음수 값을 가진 적분 나누기가 0으로 반올림됩니다.

static_assert(-5 / 2 == -2);

등등.

많은 경우에 런타임 어설 션은 정적 어설 션 대신 사용할 수 있지만 런타임 어설 션은 제어가 어설 션을 통과 할 때만 런타임에만 작동합니다. 이러한 이유로 실패한 런타임 어설 션은 오랜 기간 동안 감지되지 않고 휴면 상태가 될 수 있습니다.

물론 정적 어설 션의 표현은 컴파일 타임 상수 여야합니다. 런타임 값이 될 수 없습니다. 런타임 값의 경우 다른 선택이 없지만 일반 assert.


3
static_assert는 두 번째 매개 변수로 문자열 리터럴을 가져야하지 않습니까?
Trevor Hickey

3
@Trevor Hickey : 네, 그렇습니다. 하지만 저는 static_assertC ++ 11에서 구체적 으로 언급하려고하지 않았습니다 . 내 static_assert위의 정적 주장의 그냥 추상 구현입니다. (저는 개인적으로 C 코드에서 이와 같은 것을 사용합니다). 내 대답은 정적 단언의 일반적인 목적과 런타임 단언과의 차이점에 대한 것입니다.
AnT

첫 번째 예에서는 유형의 변수에 패딩 비트가 없다고 가정합니다 unsigned int. 이것은 표준에 의해 보장되지 않습니다. 유형의 변수는 unsigned int합법적으로 32 비트의 메모리를 차지하여 16 비트를 사용하지 않은 채로 남겨 둘 수 있습니다 (따라서 매크로 UINT_MAX65535). 따라서 첫 번째 정적 어설 션 ( " 정확히 32 비트를 가진unsigned int 객체 ") 을 설명하는 방식 은 잘못된 것입니다. 설명과 일치하려면이 주장도 포함되어야합니다 .. static_assert(UINT_MAX >= 0xFFFFFFFFu)
RalphS

@TrevorHickey 더 이상 (C ++ 17)
luizfls

13

컴파일러 동작, 헤더, libs 및 심지어 내 자신의 코드에 대한 가정이 올바른지 확인하기 위해 사용합니다. 예를 들어 여기에서는 구조체가 예상 크기로 올바르게 패킹되었는지 확인합니다.

struct LogicalBlockAddress
{
#pragma pack(push, 1)
    Uint32 logicalBlockNumber;
    Uint16 partitionReferenceNumber;
#pragma pack(pop)
};
BOOST_STATIC_ASSERT(sizeof(LogicalBlockAddress) == 6);

클래스 포장에서 stdio.hfseek(), 나는 몇 가지 바로 가기를 촬영 한 enum Origin그 바로 가기에 의해 정의 된 상수와 맞게 연결되어 있는지 체크stdio.h

uint64_t BasicFile::seek(int64_t offset, enum Origin origin)
{
    BOOST_STATIC_ASSERT(SEEK_SET == Origin::SET);

위에서 설명한 예제와 같이 동작이 런타임이 아닌 컴파일 타임에 정의되는 경우 static_assert보다 선호해야합니다 assert. 그렇지 않은 예 에는 매개 변수 및 리턴 코드 검사가 포함됩니다.

BOOST_STATIC_ASSERT조건이 충족되지 않으면 잘못된 코드를 생성하는 pre-C ++ 0x 매크로입니다. 의도는 동일하지만 static_assert표준화되고 더 나은 컴파일러 진단을 제공 할 수 있습니다.


9

BOOST_STATIC_ASSERTstatic_assert기능을 위한 크로스 플랫폼 래퍼입니다 .

현재 저는 클래스에 "개념"을 적용하기 위해 static_assert를 사용하고 있습니다.

예:

template <typename T, typename U>
struct Type
{
  BOOST_STATIC_ASSERT(boost::is_base_of<T, Interface>::value);
  BOOST_STATIC_ASSERT(std::numeric_limits<U>::is_integer);
  /* ... more code ... */
};

위의 조건 중 하나라도 충족되지 않으면 컴파일 시간 오류가 발생합니다.


3
이제 C ++ 11이 나왔으므로 (그리고 한동안 나왔습니다) static_assert는 모든 주요 컴파일러의 최신 버전에서 지원되어야합니다. C ++ 14 (템플릿 제약이 포함되기를 바랍니다)를 기다릴 수없는 사람들에게 이것은 static_assert의 매우 유용한 응용 프로그램입니다.
Collin

7

의 한 가지 용도는 static_assert구조 (네트워크 또는 파일과 같은 외부 세계와의 인터페이스)가 예상 한 크기와 정확히 일치하는지 확인하는 것입니다. 이것은 누군가가 결과를 깨닫지 못하고 구조에서 멤버를 추가하거나 수정하는 경우를 포착합니다. 은 static_assert그것을 선택하고 사용자에게 경고합니다.


3

개념이 없으면 static_assert예를 들어 템플릿에서 간단하고 읽기 쉬운 컴파일 타임 유형 검사에 사용할 수 있습니다 .

template <class T>
void MyFunc(T value)
{
static_assert(std::is_base_of<MyBase, T>::value, 
              "T must be derived from MyBase");

// ...
}

2

이것은 원래의 질문에 직접 답하지는 않지만 C ++ 11 이전에 이러한 컴파일 시간 검사를 시행하는 방법에 대한 흥미로운 연구를 만듭니다.

Andrei Alexanderscu 의 Modern C ++ Design 2 장 (섹션 2.1)은 다음과 같은 컴파일 타임 어설 션 아이디어를 구현합니다.

template<int> struct CompileTimeError;
template<> struct CompileTimeError<true> {};

#define STATIC_CHECK(expr, msg) \
{ CompileTimeError<((expr) != 0)> ERROR_##msg; (void)ERROR_##msg; } 

매크로 STATIC_CHECK () 및 static_assert () 비교

STATIC_CHECK(0, COMPILATION_FAILED);
static_assert(0, "compilation failed");

-2

는 다음과 같은 방식으로 키워드 static_assert사용을 금지하는 데 사용할 수 있습니다 delete.

#define delete static_assert(0, "The keyword \"delete\" is forbidden.");

모든 최신 C ++ 개발자는 보수적 가비지 수집기 의 보수적 힙에 메모리를 할당하는 함수를 호출하기 위해 new 연산자 를 오버로드하는 클래스 es 및 struct 만 사용하여 보수적 가비지 수집기를 사용하려는 경우이를 수행 할 수 있습니다. 함수의 시작 부분에서이를 수행하는 일부 함수를 호출하여 초기화 및 인스턴스화 할 수 있습니다 .main

예를 들어 Boehm-Demers-Weiser 보수적 가비지 수집기를 사용하려는 모든 최신 C ++ 개발자는 main함수 시작 부분에 다음과 같이 작성합니다.

GC_init();

그리고 모든 년 classstruct과부하 operator new이런 식으로 :

void* operator new(size_t size)
{
     return GC_malloc(size);
}

그리고 이제는 operator delete더 이상 필요하지 않습니다. Boehm-Demers-Weiser 보수적 가비지 수집기가 더 이상 필요하지 않을 때 모든 메모리 블록을 해제하고 할당 해제해야하기 때문에 개발자는 delete키워드 를 금지하려고 합니다.

한 가지 방법은 다음과 같이 오버로드하는 delete operator것입니다.

void operator delete(void* ptr)
{
    assert(0);
}

그러나 이것은 권장되지 않습니다. 최신 C ++ 개발자는 자신 delete operator이 런타임에 실수로를 호출했음을 알 수 있기 때문에 컴파일 타임에 이것을 곧 아는 것이 좋습니다.

따라서이 시나리오에 대한 가장 좋은 해결책 static_assert은이 답변의 시작 부분에 표시된 것처럼 사용하는 것입니다.

물론 이것은로도 할 수 BOOST_STATIC_ASSERT있지만 나는 그것이 static_assert더 낫고 항상 더 선호되어야 한다고 생각합니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.