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(...)
?
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 #define
d (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 :("
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.
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.
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.
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.
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");
// ...
}
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");