Static_assert ne yapar ve onu ne için kullanırsınız?


117

static_assert(...)('C ++ 11') 'in problemi zarif bir şekilde çözeceği bir örnek verebilir misiniz ?

Çalışma zamanına aşinayım assert(...). Tercihim ne zaman static_assert(...)düzenli üzerinde assert(...)?

Ayrıca, boostdenen bir şey var, BOOST_STATIC_ASSERTaynı static_assert(...)mı?


AYRICA BAKINIZ: Daha fazla seçenek için 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, nasıl kullanılacağını bulduğunuzda özellikle güzeldir.
KitsuneYMG

Yanıtlar:


82

Başımın üstünde ...

#include "SomeLibrary.h"

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

class UsingSomeLibrary {
   // ...
};

Bunun d SomeLibrary::Versionolmaktan ziyade statik bir const olarak bildirildiğini varsayarsak #define(bir C ++ kitaplığında beklendiği gibi).

SomeLibraryKodunuzu gerçekten derlemek , her şeyi bağlamak ve yürütülebilir dosyayı çalıştırmak zorunda kalmanın aksine, ancak o zaman uyumsuz bir sürümünü derlemek için 30 dakika harcadığınızı öğrenmek için SomeLibrary.

@Arak, yorumunuza yanıt olarak: evet, static_assertgörünüşe göre her yerde oturabilirsiniz :

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: hata: statik onaylama başarısız oldu: "Foo :: bar çok küçük :("

1
Biraz kafam karıştı, static_assertinfaz dışı bir bağlam koyabilir misin? Çok güzel bir örnek gibi görünüyor :)
AraK

3
Evet, durağan iddialar, genellikle, yalnızca yüklem doğruysa tanımlanan bir nesne oluşturmak için uygulanır. Bu sadece küresel olur.
GManNickG

Bunun orijinal soruyu bütünüyle yanıtlayacağından emin değilim, ama güzel bir gösteri
Matt Joiner

2
Bu cevap, <cassert> ile static_assert arasındaki assert arasındaki fark hakkında herhangi bir ayrıntı sağlamaz
bitek

11
@monocoder: "Kontrast ..." ile başlayan paragrafa bakın. Kısacası: assert çalışma zamanında durumunu kontrol eder ve static_assert derleme sırasında durumunu kontrol eder. Bu nedenle, öne sürdüğünüz koşul derleme zamanında biliniyorsa, kullanın static_assert. Program çalışana kadar koşul bilinmeyecekse, kullanın assert.
Mike DeSimone

131

Statik iddia, derleme sırasında iddialarda bulunmak için kullanılır. Statik iddia başarısız olduğunda, program derlenmez. Bu, örneğin, unsigned inttam olarak 32 bit olan nesneye kritik olarak bağlı olan bazı işlevleri kodla uygularsanız, farklı durumlarda kullanışlıdır . Bunun gibi statik bir iddia koyabilirsiniz

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

kodunuzda. Başka bir platformda, farklı boyutta unsigned inttipte derleme başarısız olur, böylece geliştiricinin dikkatini kodun sorunlu kısmına çeker ve onlara kodu yeniden uygulamalarını veya yeniden incelemelerini tavsiye eder.

Başka bir örnek için, void *bir işleve işaretçi olarak bazı integral değerleri iletmek isteyebilirsiniz (bir hack, ancak bazen yararlıdır) ve integral değerinin işaretçiye sığacağından emin olmak isteyebilirsiniz.

int i;

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

Bu chartürün imzalı olduğunu belirtmek isteyebilirsiniz

static_assert(CHAR_MIN < 0);

veya negatif değerli integral bölme sıfıra yuvarlanır

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

Ve bunun gibi.

Çoğu durumda çalışma zamanı iddiaları, statik iddialar yerine kullanılabilir, ancak çalışma zamanı iddiaları yalnızca çalışma zamanında ve yalnızca denetim iddianın üzerinden geçtiğinde çalışır. Bu nedenle, başarısız bir çalışma zamanı iddiası, uzun süreler boyunca fark edilmeden uykuda kalabilir.

Elbette, statik iddiadaki ifade bir derleme zamanı sabiti olmalıdır. Çalışma zamanı değeri olamaz. Çalışma zamanı değerleri için sıradan olanı kullanmaktan başka seçeneğiniz yoktur assert.


3
Static_assert ikinci parametre olarak bir dizge olması GEREKMEZ mi?
Trevor Hickey

3
@Trevor Hickey: Evet, öyle. Ama static_assertözellikle C ++ 11'den bahsetmeye çalışmıyordum . Benim static_assertyukarıda statik onaylamanın sadece bazı soyut uygulamasıdır. (Ben şahsen C kodunda böyle bir şey kullanıyorum). Cevabım, statik iddiaların genel amacı ve çalışma zamanı iddialarından farkları ile ilgili olacak.
AnT

İlk örnekte, bir tür değişkeninde dolgu biti olmadığını varsayarsınız unsigned int. Bu, standart tarafından garanti edilmez. Bir tür değişkeni unsigned intyasal olarak 32 bit hafızayı işgal edebilir ve bunlardan 16'sını kullanılmadan bırakır (ve böylece makro UINT_MAXeşit olur 65535). Dolayısıyla, ilk statik önermeyi (" tam olarak 32 bit olanunsigned int nesne ") açıklama şekliniz yanıltıcıdır. Açıklamanızı eşleştirmek için, bu sav yanı dahil edilmelidir: . static_assert(UINT_MAX >= 0xFFFFFFFFu)
RalphS

@TrevorHickey artık değil (C ++ 17)
luizfls

13

Derleyici davranışı, başlıklar, kitaplıklar ve hatta kendi kodum hakkındaki varsayımlarımın doğru olmasını sağlamak için kullanıyorum. Örneğin burada yapının beklenen boyutta doğru bir şekilde paketlendiğini doğruluyorum.

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

Bir sınıf ambalajda stdio.h'ın fseek(), ben bazı kısayollar almış enum Originolanlar kısayolları ile tanımlanan sabitler hizada olmasını ve çekstdio.h

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

Sen tercih etmeliyiz static_assertüzerinde assertdavranış böyle Yukarıda verdiğim örnek olarak, derleme zamanında tanımlanan ve çalışma anında zaman. Durumun böyle olmadığı bir örnek , parametre ve dönüş kodu kontrolünü içerir.

BOOST_STATIC_ASSERTkoşul yerine getirilmezse geçersiz kod üreten bir pre-C ++ 0x makrosudur. static_assertStandartlaştırılmış olsa da niyetler aynıdır ve daha iyi derleyici teşhisi sağlayabilir.


9

BOOST_STATIC_ASSERTstatic_assertişlevsellik için bir çapraz platform sarmalayıcıdır .

Şu anda bir sınıfa "Kavramları" zorlamak için static_assert kullanıyorum.

misal:

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

Bu, yukarıdaki koşullardan herhangi biri karşılanmazsa bir derleme zamanı hatasına neden olur.


3
Artık C ++ 11 çıktı (ve bir süredir kullanımda), static_assert tüm büyük derleyicilerin daha yeni sürümleri tarafından desteklenmelidir. C ++ 14'ü bekleyemeyenler için (ki umarız şablon kısıtlamaları içerir), bu static_assert'in çok kullanışlı bir uygulamasıdır.
Collin

7

Bunun bir kullanımı, static_assertbir yapının (ağ veya dosya gibi dış dünya ile bir arayüz olan) tam olarak beklediğiniz boyutta olmasını sağlamak olabilir. Bu, bir kişinin sonuçları fark etmeden yapıdan bir üye eklediği veya değiştirdiği durumları yakalayacaktır. static_assertOnu almak ve kullanıcıyı uyarmak istiyorum.


3

Kavramların yokluğunda, static_assertörneğin şablonlarda basit ve okunabilir derleme zamanı tür denetimi için kullanılabilir:

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

// ...
}

2

Bu, orijinal soruyu doğrudan yanıtlamaz, ancak C ++ 11'den önce bu derleme zamanı kontrollerinin nasıl uygulanacağına dair ilginç bir çalışma yapar.

Andrei Alexanderscu'nun hazırladığı Modern C ++ Tasarımı Bölüm 2 (Kısım 2.1), bu gibi Derleme zamanı iddiaları fikrini uygulamaktadır.

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

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

STATIC_CHECK () ve static_assert () makrosunu karşılaştırın

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

-2

static_assertKullanımını yasaklamak için kullanılabilecek deleteanahtar kelime bu şekilde:

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

Her modern C ++ geliştiricisi, muhafazakar çöp toplayıcının muhafazakar yığınına bellek ayıran bir işlevi çağırmak için new operatörünü aşırı yükleyen yalnızca sınıflar ve yapılar kullanarak muhafazakar bir çöp toplayıcı kullanmak istiyorsa bunu yapmak isteyebilir. işlevin başlangıcında bunu yapan bir işlev çağırılarak başlatılabilir ve somutlaştırılabilir .main

Örneğin, Boehm-Demers-Weiser muhafazakar çöp toplayıcısını kullanmak isteyen her modern C ++ geliştiricisi, mainişlevin başında şunu yazacaktır:

GC_init();

Ve her classve structaşırı operator newbu şekilde:

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

Ve artık operator deletebuna gerek olmadığına göre, Boehm-Demers-Weiser muhafazakar çöp toplayıcı, artık ihtiyaç duyulmadığında her bellek bloğunu hem serbest bırakmak hem de serbest bırakmaktan sorumludur, geliştiricidelete anahtar kelimeyi .

Bunun bir yolu, delete operatorbu şekilde aşırı yüklemektir :

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

Ancak bu tavsiye edilmez, çünkü modern C ++ geliştiricisi yanlışlıkla delete operatorçalışma zamanını çalıştırdığını bilecektir, ancak bunu derleme zamanında yakında bilmek daha iyidir.

Bence bu senaryoya en iyi çözüm, static_assertcevabın başında gösterildiği gibi kullanmaktır .

Tabii bununla da yapılabilir BOOST_STATIC_ASSERTama bence static_assertdaha iyi ve her zaman daha çok tercih edilmeli.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.