2009-10-30 16 views
72

¿Podría dar un ejemplo donde static_assert(...) 'C++0x' resolvería el problema en la mano con elegancia?¿Qué hace static_assert y para qué lo usaría?

Estoy familiarizado con el tiempo de ejecución assert(...). ¿Cuándo debería preferir static_assert(...) sobre el assert(...) normal?

Además, en boost hay algo llamado BOOST_STATIC_ASSERT, ¿es lo mismo que static_assert(...)?

+0

VEA TAMBIÉN: 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] para más opciones. _MSG es especialmente bueno una vez que descubres cómo usarlo. – KitsuneYMG

Respuesta

50

De la parte superior de mi cabeza ...

#include "SomeLibrary.h" 

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

class UsingSomeLibrary { 
    // ... 
}; 

Suponiendo que SomeLibrary::Version se declara como static const, en lugar de ser #define d (como es de esperar en una biblioteca de C++).

contraste con la realidad de tener que recopilar SomeLibrary y su código, vincular todo, y ejecute el archivo ejecutable solamente continuación para averiguar que pasó 30 minutos compilar una versión incompatible de SomeLibrary.

@Arak, en respuesta a su comentario: sí, puede tener static_assert simplemente sentarse donde sea, desde el aspecto de la misma:

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

Estoy un poco confundido, ¿puedes poner 'static_assert' en un contexto de no ejecución?Parece un muy buen ejemplo :) – AraK

+2

Sí, las afirmaciones estáticas tal como están se suelen implementar como la creación de un objeto que solo se define si el predicado es verdadero. Esto solo haría un global. – GManNickG

+0

no estoy seguro de que esto califique como respuesta a la pregunta original en su totalidad, pero buena demostración –

3

Un uso de static_assert podría ser asegurar que una estructura (que es una interfaz con el mundo exterior, como una red o un archivo) tenga exactamente el tamaño que usted espera. Esto captaría los casos en que alguien agrega o modifica a un miembro de la estructura sin darse cuenta de las consecuencias. El static_assert lo recogería y alertaría al usuario.

10

lo uso para asegurar mis suposiciones sobre el comportamiento del compilador , encabezados, libs e incluso mi propio código son correctos. Por ejemplo, aquí verifico que la estructura se ha empaquetado correctamente al tamaño esperado.

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

En un envoltorio clase stdio.h 's fseek(), he tomado algunos atajos con enum Origin y comprobar que los atajos se alinean con las constantes definidas por stdio.h

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

Usted debe preferir static_assert sobre assert cuando el el comportamiento se define en tiempo de compilación, y no en tiempo de ejecución, como los ejemplos que he dado arriba. Un ejemplo donde esto es no el caso incluiría el parámetro y la verificación del código de retorno.

es una macro pre-C++ 0x que genera código ilegal si la condición no se cumple. Las intenciones son las mismas, aunque static_assert está estandarizado y puede proporcionar mejores diagnósticos del compilador.

96

La afirmación estática se utiliza para realizar afirmaciones en tiempo de compilación. Cuando la aserción estática falla, el programa simplemente no compila. Esto es útil en diferentes situaciones, como, por ejemplo, si implementa alguna funcionalidad por código que depende críticamente del objeto unsigned int que tiene exactamente 32 bits.Puede poner una afirmación estática como esta

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

en su código. En otra plataforma, con el tipo de unsigned int de diferente tamaño, la compilación fallará, llamando la atención del desarrollador sobre la parte problemática del código y aconsejándoles que lo vuelvan a implementar o vuelvan a inspeccionar.

Para otro ejemplo, es posible que desee pasar algún valor integral como un puntero void * a una función (un truco, pero útil a veces) y que desea asegurarse de que el valor integral encajará en el puntero

int i; 

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

es posible que desee activo que char tipo se firmó

static_assert(CHAR_MIN < 0); 

o que la división integral con valores negativos redondea hacia cero

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

Y así sucesivamente.

Las aserciones en tiempo de ejecución en muchos casos se pueden usar en lugar de aserciones estáticas, pero las aserciones en tiempo de ejecución solo funcionan en tiempo de ejecución y solo cuando el control pasa por encima de la aserción. Por esta razón, una afirmación de tiempo de ejecución anómala puede permanecer latente, no detectada durante largos periodos de tiempo.

Por supuesto, la expresión en la aserción estática debe ser una constante en tiempo de compilación. No puede ser un valor de tiempo de ejecución. Para los valores de tiempo de ejecución, no tiene otra opción que usar el ordinario assert.

+0

Esta es una buena respuesta, espero que sepa que obtuvo mi +1 :) – AraK

+1

¿No es REQUERIDO static_assert tener una cadena literal como un segundo parámetro? –

+0

@Trevor Hickey: Sí, lo es. Pero no estaba tratando de referirme a 'static_assert' de C++ 11 específicamente. Mi 'static_assert' arriba es solo una implementación abstracta de aserción estática. (Yo personalmente uso algo así en el código C). Mi respuesta pretende ser sobre el propósito general de las afirmaciones estáticas y su diferencia de las aserciones en tiempo de ejecución. – AnT

9

BOOST_STATIC_ASSERT es una envoltura multiplataforma para la funcionalidad static_assert.

Actualmente estoy usando static_assert para aplicar "Conceptos" en una clase.

ejemplo:

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

Esto provocará un error de tiempo de compilación si no se cumple cualquiera de las condiciones anteriores.

+3

Ahora que C++ 11 está fuera (y ha estado fuera por un tiempo), static_assert debe ser compatible con las versiones más recientes de todos los compiladores principales. Para aquellos de nosotros que no podemos esperar para C++ 14 (que con suerte contendrá restricciones de plantilla), esta es una aplicación muy útil de static_assert. – Collin

2

Esto no responde directamente a la pregunta original, pero hace un interesante estudio sobre cómo hacer cumplir estas comprobaciones de tiempo de compilación antes de C++ 11.

Capítulo 2 (Sección 2.1) de Modern C++ Design por Andrei Alexanderscu implementa esta idea de afirmaciones en tiempo de compilación como esta

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

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

comparar la STATIC_CHECK macro() y static_assert()

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

En ausencia de conceptos se puede usar static_assert para una verificación de tipos de tiempo de compilación simple y legible, por ejemplo, en plantillas:

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

// ... 
} 
Cuestiones relacionadas