2009-10-30 13 views
72

Czy możesz podać przykład, w którym static_assert(...) 'C++0x' rozwiąże problem w sposób elegancko?Co robi static_assert i na co byś go używał?

Jestem zaznajomiony z run-time assert(...). Kiedy powinienem preferować numer static_assert(...) zamiast zwykłego assert(...)?

Również w boost jest coś, co nazywa się BOOST_STATIC_ASSERT, czy jest takie samo jak static_assert(...)?

+0

ZOBACZ TAKŻE: BOOST_MPL_ASSERT, BOOST_MPL_ASSERT_NOT, BOOST_MPL_ASSERT_MSG, BOOST_MPL_ASSERT_RELATION [http://www.boost.org/doc/libs/1_40_0/libs/mpl/doc/refmanual/asserts.html], aby uzyskać więcej opcji. _MSG jest szczególnie przyjemny, gdy już się go nauczysz. – KitsuneYMG

Odpowiedz

50

Off szczycie mojej głowie ...

#include "SomeLibrary.h" 

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

class UsingSomeLibrary { 
    // ... 
}; 

Zakładając, że SomeLibrary::Version jest zadeklarowana jako static const, zamiast być #define d (jak można by oczekiwać w bibliotece C++).

Kontrast z konieczności faktycznie kompilacji SomeLibrary i kod, połączyć wszystko i uruchom plik wykonywalny tylko następnie aby dowiedzieć się, że spędził 30 minut kompilacji niezgodną wersję SomeLibrary.

@Arak, w odpowiedzi na Twój komentarz: tak, można mieć static_assert tylko siedzi się gdziekolwiek, z wyglądu go:

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: error: static assertion failed: "Foo::bar is too small :(" 
+1

Jestem trochę zdezorientowany, możesz umieścić 'static_assert' w kontekście niewykonywania?Wydaje się to bardzo ładnym przykładem :) – AraK

+2

Tak, statyczne twierdzenia w stanie, w jakim się znajdują, są zwykle implementowane jako tworzenie obiektu, który jest zdefiniowany tylko wtedy, gdy predykat jest prawdziwy. To po prostu stałoby się globalne. – GManNickG

+0

Nie jestem pewien, czy kwalifikuje się to jako odpowiedź na oryginalne pytanie w całości, ale miła demonstracja –

3

Jednym z zastosowań static_assert może być zapewnienie, że struktura (która jest interfejsem ze światem zewnętrznym, takim jak sieć lub plik) ma dokładnie taki rozmiar, jakiego się spodziewasz. Może to uchwycić przypadki, w których ktoś dodaje lub modyfikuje członka ze struktury bez uświadomienia sobie konsekwencji. Model static_assert podniósłby go i ostrzegł użytkownika.

10

go używać, aby zapewnić moje założenia dotyczące zachowania kompilatora , nagłówki, biblioteki i nawet mój własny kod są poprawne. Na przykład tutaj sprawdzam, czy struktura została poprawnie spakowana do oczekiwanego rozmiaru.

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

W opakowaniu klasy stdio.h „s fseek(), że podjęły pewne skróty z enum Origin i sprawdzić, że te skróty wyrównać ze stałych zdefiniowanych przez stdio.h

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

powinny Wolisz static_assert nad assert gdy zachowanie jest zdefiniowane podczas kompilacji, a nie w czasie wykonywania, tak jak w przykładach podanych powyżej. Przykład, w którym jest to przypadek, obejmowałby sprawdzanie kodu parametru i kodu powrotu.

BOOST_STATIC_ASSERT to makro poprzedzające C++ 0x, które generuje nieprawidłowy kod, jeśli warunek nie jest spełniony. Intencje są takie same, choć static_assert jest ustandaryzowane i może zapewnić lepszą diagnostykę kompilatora.

96

Stwierdzenie statyczne służy do tworzenia asercji w czasie kompilacji. Kiedy asercja statyczna kończy się niepowodzeniem, program po prostu się nie kompiluje. Jest to przydatne w różnych sytuacjach, na przykład w przypadku implementacji niektórych funkcjonalności za pomocą kodu, który jest krytycznie zależny od obiektu o dokładnie 32-bitowej wartości.W swoim kodzie możesz wstawić statyczny dysk twardy, taki jak ten Na innej platformie, o różnej wielkości typie unsigned int kompilacja się nie powiedzie, co zwróci uwagę autora na problematyczną część kodu i zaleci im ponowne wdrożenie lub ponowną inspekcję.

Inny przykład, może chcesz przekazać jakąś integralną wartość jako void * wskaźnik do funkcji (hack, ale przydatny czasami) i chcesz się upewnić, że wartość integralną będzie pasować do wskaźnika

int i; 

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

Możesz chcieć aktywów, który char typu podpisanym

static_assert(CHAR_MIN < 0); 

lub że integralną podział z wartościami ujemnymi zaokrągla do zera

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

I tak dalej.

Asercje czasu pracy w wielu przypadkach mogą być używane zamiast asercji statycznych, ale asercje wykonawcze działają tylko w czasie wykonywania i tylko wtedy, gdy kontrola przechodzi nad asercją. Z tego powodu twierdzenie o niepowodzeniu w działaniu może być uśpione, niewykryte przez dłuższy czas.

Oczywiście wyrażenie w asercji statycznej musi być stałą czasu kompilacji. Nie może to być wartość czasu wykonywania. Dla wartości wykonawczych nie masz innego wyboru niż zwykły assert.

+0

To jest fajna odpowiedź na pewno, mam nadzieję, że wiesz, że dostałeś moje +1 :) – AraK

+1

Czy static_assert nie jest WYMAGANE, aby literał tekstowy był drugim parametrem? –

+0

@ Trevor Hickey: Tak, jest. Ale nie próbowałem odnieść się w szczególności do 'static_assert' z C++ 11. Moje 'static_assert' powyżej to po prostu abstrakcyjna implementacja asercji statycznej. (Osobiście używam czegoś takiego w kodzie C). Moja odpowiedź ma dotyczyć ogólnego celu asercji statycznych i ich różnicy od asercji wykonawczych. – AnT

9

BOOST_STATIC_ASSERT to opakowanie dla różnych platform dla funkcji static_assert.

Obecnie używam static_assert w celu wymuszenia "Concepts" na klasie.

przykład:

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 ... */ 
}; 

To spowoduje błąd kompilacji czas, jeśli którykolwiek z powyższych warunków nie jest spełniony.

+3

Teraz, gdy C++ 11 jest wyłączony (i był przez jakiś czas niedostępny), static_assert powinno być wspierane przez nowsze wersje wszystkich głównych kompilatorów. Dla tych z nas, którzy nie mogą się doczekać C++ 14 (które, mam nadzieję, będą zawierać ograniczenia szablonów), jest to bardzo przydatna aplikacja static_assert. – Collin

2

To nie odpowiada bezpośrednio na oryginalne pytanie, ale stanowi interesujące studium, w jaki sposób wymusić te kontrole czasu kompilacji przed C++ 11.

Rozdział 2 (sekcja 2.1) z Modern C++ Design Andrieja Alexanderscu realizuje tę ideę twierdzeń kompilacji takiego

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

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

Porównaj makro STATIC_CHECK() i static_assert()

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

W brak koncepcji można użyć static_assert do prostego i czytelnego sprawdzania typu kompilacji, na przykład w szablonach:

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

// ... 
}