web-dev-qa-db-fra.com

Que fait static_assert et à quoi l'utiliseriez-vous?

Pourriez-vous donner un exemple où static_assert(...) 'C++0x' résoudrait le problème avec élégance?

Je connais le temps d'exécution assert(...). Quand devrais-je préférer static_assert(...) à un assert(...) ordinaire?

De plus, dans boost il y a quelque chose qui s'appelle BOOST_STATIC_ASSERT, Est-ce la même chose que static_assert(...)?

105
AraK

Du haut de ma tête...

#include "SomeLibrary.h"

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

class UsingSomeLibrary {
   // ...
};

En admettant que SomeLibrary::Version est déclaré comme une constante statique, plutôt que d'être #defined (comme on pourrait s'y attendre dans une bibliothèque C++).

Contrairement à la nécessité de compiler SomeLibrary et votre code, de tout lier et d'exécuter l'exécutable uniquement then pour découvrir que vous avez passé 30 minutes à compiler une version incompatible de SomeLibrary.

@Arak, en réponse à votre commentaire: oui, vous pouvez avoir static_assert simplement assis n'importe où, d'après son apparence:

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: erreur: l'assertion statique a échoué: "Foo :: bar est trop petit :(" 
68
Mark Rushakoff

L'assertion statique est utilisée pour effectuer des assertions au moment de la compilation. Lorsque l'assertion statique échoue, le programme ne compile tout simplement pas. Ceci est utile dans différentes situations, comme, par exemple, si vous implémentez une fonctionnalité par code qui dépend de manière critique de unsigned int objet ayant exactement 32 bits. Vous pouvez mettre une assertion statique comme celle-ci

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

dans votre code. Sur une autre plate-forme, avec une taille différente unsigned int taper la compilation échouera, attirant ainsi l'attention du développeur sur la partie problématique du code et lui conseillant de le réimplémenter ou de le ré-inspecter.

Pour un autre exemple, vous souhaiterez peut-être passer une valeur intégrale en tant que void * pointeur vers une fonction (un hack, mais parfois utile) et vous voulez vous assurer que la valeur intégrale rentrera dans le pointeur

int i;

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

Vous souhaiterez peut-être associer le type char à la signature

static_assert(CHAR_MIN < 0);

ou cette division intégrale avec des valeurs négatives arrondit vers zéro

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

Etc.

Dans de nombreux cas, les assertions d'exécution peuvent être utilisées à la place d'assertions statiques, mais les assertions d'exécution ne fonctionnent qu'au moment de l'exécution et uniquement lorsque le contrôle passe sur l'assertion. Pour cette raison, une assertion d'exécution défaillante peut rester inactive, non détectée pendant de longues périodes.

Bien sûr, l'expression dans l'assertion statique doit être une constante de temps de compilation. Il ne peut pas s'agir d'une valeur d'exécution. Pour les valeurs d'exécution, vous n'avez pas d'autre choix que d'utiliser le assert ordinaire.

120
AnT

Je l'utilise pour garantir que mes hypothèses sur le comportement du compilateur, les en-têtes, les bibliothèques et même mon propre code sont corrects. Par exemple ici, je vérifie que la structure a été correctement compressée à la taille attendue.

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

Dans une classe encapsulant la fseek() de stdio.h, J'ai pris quelques raccourcis avec enum Origin Et je vérifie que ces raccourcis s'alignent avec les constantes définies par stdio.h

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

Vous devriez préférer static_assert À assert lorsque le comportement est défini au moment de la compilation, et non au moment de l'exécution, comme les exemples que j'ai donnés ci-dessus. Un exemple où ceci est pas le cas inclurait la vérification des paramètres et du code retour.

BOOST_STATIC_ASSERT Est une macro pré-C++ 0x qui génère du code illégal si la condition n'est pas remplie. Les intentions sont les mêmes, bien que static_assert Soit standardisé et puisse fournir de meilleurs diagnostics du compilateur.

12
Matt Joiner

BOOST_STATIC_ASSERT est un wrapper multiplateforme pour static_assert fonctionnalité.

Actuellement, j'utilise static_assert afin d'appliquer des "Concepts" à une classe.

exemple:

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

Cela entraînera une erreur de temps de compilation si l'une des conditions ci-dessus n'est pas remplie.

9
nurettin

Une utilisation de static_assert pourrait être de s'assurer qu'une structure (c'est-à-dire une interface avec le monde extérieur, comme un réseau ou un fichier) a exactement la taille que vous attendez. Cela permettrait de détecter les cas où quelqu'un ajoute ou modifie un membre de la structure sans en réaliser les conséquences. Le static_assert le prendrait et alerterait l'utilisateur.

7
Greg Hewgill

En l'absence de concepts, on peut utiliser static_assert pour une vérification de type de compilation simple et lisible, par exemple dans les modèles:

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

// ...
}
3
vladon

Cela ne répond pas directement à la question d'origine, mais fait une étude intéressante sur la façon d'appliquer ces vérifications de temps de compilation avant C++ 11.

Le chapitre 2 (section 2.1) de Design C++ moderne par Andrei Alexanderscu implémente cette idée d'assertions à la compilation comme celle-ci

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

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

Comparez la macro STATIC_CHECK () et static_assert ()

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