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_assertC ++ 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_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 ... */
};
위의 조건 중 하나라도 충족되지 않으면 컴파일 시간 오류가 발생합니다.
이것은 원래의 질문에 직접 답하지는 않지만 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더 낫고 항상 더 선호되어야 한다고 생각합니다 .