¿Qué hace static assert y para qué lo utilizarías?


¿Podría dar un ejemplo donde static_assert(...) 'C++0x' resolvería el problema con elegancia?

Estoy familiarizado con el tiempo de ejecución assert(...). ¿Cuándo debo preferir static_assert(...) a assert(...) regular?

También, en boost hay algo llamado BOOST_STATIC_ASSERT, ¿es lo mismo que static_assert(...)?

Author: AraK, 2009-10-30

7 answers

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::Versionse declara como una const estática, en lugar de ser #define d (como cabría esperar en una biblioteca de C++).

En contraste con tener que realmente compilar SomeLibraryy su código, vincular todo, y ejecutar el ejecutable solo luego para descubrir que pasó 30 minutos compilando una versión incompatible de SomeLibrary.

@Arak, en respuesta a tu comentario: sí, puedes tener static_assert simplemente sentado donde sea, desde la mirada 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 :("
 60
Author: Mark Rushakoff,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-10-30 04:01:22

Static assert se usa para hacer aserciones 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 de unsigned int objeto 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 unsigned int de diferente tamaño, la compilación fallará, lo que llamará la atención del desarrollador a la parte problemática del código y aconsejarles que lo vuelvan a implementar o inspeccionen.

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 desea asegurarse de que el valor integral quepa en el puntero

int i;

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

Es posible que desee asset que el tipo char está firmado

static_assert(CHAR_MIN < 0);

O esa división integral con valores negativos redondea hacia cero

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

Y así en.

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 sobre la aserción. Por esta razón, una afirmación fallida en tiempo de ejecución puede permanecer inactiva, sin ser detectada durante largos períodos de tiempo.

Por supuesto, la expresión en la aserción estática tiene que 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 assert ordinario.

 108
Author: AnT,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-10-30 04:03:16

Lo uso para asegurar que mis suposiciones sobre el comportamiento del compilador, encabezados, libs e incluso mi propio código sean correctas. Por ejemplo, aquí verifico que la estructura ha sido empaquetada 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 una clase que envuelve stdio.h's fseek(), he tomado algunos atajos con enum Origin y compruebo que esos 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);

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

BOOST_STATIC_ASSERT 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 un mejor diagnóstico del compilador.

 11
Author: Matt Joiner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-10-30 10:28:49

BOOST_STATIC_ASSERT es un contenedor multiplataforma para la funcionalidad static_assert.

Actualmente estoy usando static_assert para imponer "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 causará un error de tiempo de compilación si no se cumple alguna de las condiciones anteriores.

 9
Author: nurettin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-12 12:18:36

Un uso de static_assert podría ser para asegurarse de que una estructura (que es una interfaz con el mundo exterior, como una red o un archivo) es exactamente el tamaño que espera. Esto captaría casos en los 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.

 4
Author: Greg Hewgill,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-10-30 03:43:31

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

El capítulo 2 (Sección 2.1) de Modern C++ Design de Andrei Alexanderscu implementa esta idea de aserciones 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; } 

Compara la macro STATIC_CHECK () y static_assert ()

STATIC_CHECK(0, COMPILATION_FAILED);
static_assert(0, "compilation failed");
 2
Author: nightlytrails,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-02 03:53:42

En ausencia de conceptos se puede usar static_assert para una comprobación de tipos en 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");

// ...
}
 2
Author: vladon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-14 20:54:39