static_assert(...)
( 'C ++ 11')이 문제를 우아하게 해결할 수있는 예를 들어 주 시겠습니까?
나는 런타임에 익숙합니다 assert(...)
. 언제 static_assert(...)
일반보다 선호해야 assert(...)
합니까?
또한 boost
라는 BOOST_STATIC_ASSERT
것이 있습니다 static_assert(...)
.
static_assert(...)
( 'C ++ 11')이 문제를 우아하게 해결할 수있는 예를 들어 주 시겠습니까?
나는 런타임에 익숙합니다 assert(...)
. 언제 static_assert(...)
일반보다 선호해야 assert(...)
합니까?
또한 boost
라는 BOOST_STATIC_ASSERT
것이 있습니다 static_assert(...)
.
답변:
내 머릿속에서 ...
#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가 너무 작습니다 :("
static_assert
습니다. 비 실행 컨텍스트에 넣을 수 있습니까? 아주 좋은 예인 것 같습니다. :)
static_assert
. 프로그램이 실행될 때까지 조건을 알 수없는 경우 assert
.
정적 어설 션은 컴파일 타임에 어설 션을 만드는 데 사용됩니다. 정적 어설 션이 실패하면 프로그램은 단순히 컴파일되지 않습니다. 이는 예를 들어 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
.
static_assert
C ++ 11에서 구체적 으로 언급하려고하지 않았습니다 . 내 static_assert
위의 정적 주장의 그냥 추상 구현입니다. (저는 개인적으로 C 코드에서 이와 같은 것을 사용합니다). 내 대답은 정적 단언의 일반적인 목적과 런타임 단언과의 차이점에 대한 것입니다.
unsigned int
. 이것은 표준에 의해 보장되지 않습니다. 유형의 변수는 unsigned int
합법적으로 32 비트의 메모리를 차지하여 16 비트를 사용하지 않은 채로 남겨 둘 수 있습니다 (따라서 매크로 UINT_MAX
는 65535
). 따라서 첫 번째 정적 어설 션 ( " 정확히 32 비트를 가진unsigned int
객체 ") 을 설명하는 방식 은 잘못된 것입니다. 설명과 일치하려면이 주장도 포함되어야합니다 .. static_assert(UINT_MAX >= 0xFFFFFFFFu)
컴파일러 동작, 헤더, libs 및 심지어 내 자신의 코드에 대한 가정이 올바른지 확인하기 위해 사용합니다. 예를 들어 여기에서는 구조체가 예상 크기로 올바르게 패킹되었는지 확인합니다.
struct LogicalBlockAddress
{
#pragma pack(push, 1)
Uint32 logicalBlockNumber;
Uint16 partitionReferenceNumber;
#pragma pack(pop)
};
BOOST_STATIC_ASSERT(sizeof(LogicalBlockAddress) == 6);
클래스 포장에서 stdio.h
의 fseek()
, 나는 몇 가지 바로 가기를 촬영 한 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
표준화되고 더 나은 컴파일러 진단을 제공 할 수 있습니다.
BOOST_STATIC_ASSERT
static_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 ... */
};
위의 조건 중 하나라도 충족되지 않으면 컴파일 시간 오류가 발생합니다.
이것은 원래의 질문에 직접 답하지는 않지만 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");
는 다음과 같은 방식으로 키워드 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();
그리고 모든 년 class
및 struct
과부하 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
더 낫고 항상 더 선호되어야 한다고 생각합니다 .