Atributos de C++

C++ estándar define un conjunto común de atributos. También permite a los proveedores de compiladores definir sus propios atributos dentro de un espacio de nombres específico del proveedor. Sin embargo, los compiladores solo son necesarios para reconocer los atributos definidos en el estándar.

En algunos casos, los atributos estándar se superponen con parámetros __declspec específicos del compilador. En Microsoft C++, puede usar el atributo [[deprecated]] en lugar de usar __declspec(deprecated). Cualquier compilador compatible reconoce el atributo [[deprecated]]. Para todos los demás parámetros __declspec, como dllimport y dllexport, hasta ahora no hay ningún atributo equivalente, por lo que debe seguir usando la sintaxis __declspec. Los atributos no afectan al sistema de tipos y no cambian el significado de un programa. Los compiladores omiten los valores de atributo que no reconocen.

Visual Studio 2017, versión 15.3 y posteriores (disponible con /std:c++17 y versiones posteriores): en el ámbito de una lista de atributos, puede especificar el espacio de nombres de todos los nombres con un único introductor using:

void g() {
    [[using rpr: kernel, target(cpu,gpu)]] // equivalent to [[ rpr::kernel, rpr::target(cpu,gpu) ]]
    do task();
}

Atributos estándar de C++

En C++11, los atributos proporcionan una manera estandarizada de anotar construcciones de C++ (incluidas, entre otras, clases, funciones, variables y bloques) con información adicional. Los atributos pueden o no ser específicos del proveedor. Un compilador puede usar esta información para generar mensajes informativos o para aplicar lógica especial al compilar el código con atributos. El compilador omite los atributos que no reconoce, lo que significa que no puede definir sus propios atributos personalizados mediante esta sintaxis. Los atributos se incluyen entre corchetes dobles:

[[deprecated]]
void Foo(int);

Los atributos representan una alternativa estandarizada a extensiones específicas del proveedor, como directivas #pragma, __declspec() (Visual C++) o __attribute__ (GNU). Sin embargo, para la mayoría de los propósitos, tendrá que usar las construcciones específicas del proveedor. Actualmente, el estándar especifica los siguientes atributos que debe reconocer un compilador conforme.

[[carries_dependency]]

El [[carries_dependency]] atributo especifica que la función propaga el orden de dependencia de datos para la sincronización de subprocesos. El atributo se puede aplicar a uno o varios parámetros para especificar que el argumento pasado lleva una dependencia en el cuerpo de la función. El atributo se puede aplicar a la propia función para especificar que el valor devuelto lleva una dependencia fuera de la función. El compilador puede usar esta información para generar código más eficaz.

[[deprecated]]

Visual Studio 2015 y versiones posteriores: el [[deprecated]] atributo especifica que una función no está pensada para su uso. O bien, es posible que no exista en versiones futuras de una interfaz de biblioteca. El [[deprecated]] atributo se puede aplicar a la declaración de una clase, un typedef-name, una variable, un miembro de datos no estático, una función, un espacio de nombres, una enumeración, un enumerador o una especialización de plantilla. El compilador puede usar este atributo para generar un mensaje informativo cuando el código de cliente intenta llamar a la función. Cuando el compilador de Microsoft C++ detecta el uso de un [[deprecated]] elemento, genera la advertencia del compilador C4996.

[[fallthrough]]

Visual Studio 2017 y versiones posteriores: (disponible con /std:c++17 y versiones posteriores). El atributo [[fallthrough]] se puede usar en el contexto de las instrucciones switch como una sugerencia para el compilador (o cualquier persona que lea el código) de que el comportamiento de fallthrough está previsto. Actualmente, el compilador de Microsoft C++ no advierte sobre el comportamiento de paso explícito, por lo que este atributo no tiene ningún efecto sobre el comportamiento del compilador.

[[likely]]

Visual Studio 2019, versión 16.6 y posteriores: (disponible con /std:c++20 y versiones posteriores). El [[likely]] atributo especifica una sugerencia al compilador de que es más probable que se ejecute la ruta de acceso del código para la etiqueta o instrucción con atributos que las alternativas. En el compilador de Microsoft, el atributo [[likely]] marca los bloques como "código frecuente", lo que incrementa una puntuación de optimización interna. La puntuación se incrementa más al optimizar la velocidad y no tanto al optimizar el tamaño. La puntuación neta afecta a la probabilidad de insertar, desenrollar bucles y vectorizar optimizaciones. El efecto de [[likely]] y [[unlikely]] es similar a la característica Optimización guiada por perfiles, pero limitado en el ámbito a la unidad de traducción actual. La optimización del reordenamiento de bloques aún no se implementa para este atributo.

[[maybe_unused]]

Visual Studio 2017, versión 15.3 y posteriores: (disponible con /std:c++17 y versiones posteriores). El [[maybe_unused]] atributo especifica que una variable, función, clase, typedef, miembro de datos no estático, enumeración o especialización de plantilla puede no usarse intencionadamente. El compilador no advierte cuando no se usa una entidad marcada como [[maybe_unused]]. Una entidad declarada sin el atributo se puede volver a declarar posteriormente con el atributo y viceversa. Una entidad se considera marcada después de que se analiza su primera declaración marcada como [[maybe_unused]] y para el resto de la unidad de traducción actual.

[[nodiscard]]

Visual Studio 2017, versión 15.3 y posteriores: (disponible con /std:c++17 y versiones posteriores). Especifica que el valor devuelto de una función no está destinado a ser descartado. Genera la advertencia C4834, como se muestra en este ejemplo:

[[nodiscard]]
int foo(int i) { return i * i; }

int main()
{
    foo(42); //warning C4834: discarding return value of function with 'nodiscard' attribute
    return 0;
}

[[noreturn]]

El [[noreturn]] atributo especifica que una función nunca devuelve; en otras palabras, siempre produce una excepción o sale. El compilador puede ajustar sus reglas de compilación para las entidades [[noreturn]].

[[unlikely]]

Visual Studio 2019, versión 16.6 y posteriores: (disponible con /std:c++20 y versiones posteriores). El [[unlikely]] atributo especifica una sugerencia al compilador de que es menos probable que se ejecute la ruta de acceso del código para la etiqueta o instrucción con atributos que las alternativas. En el compilador de Microsoft, el atributo [[unlikely]] marca los bloques como "código poco frecuente", lo que reduce una puntuación de optimización interna. La puntuación se reduce más al optimizar el tamaño y no tanto al optimizar la velocidad. La puntuación neta afecta a la probabilidad de insertar, desenrollar bucles y vectorizar optimizaciones. La optimización del reordenamiento de bloques aún no se implementa para este atributo.

Atributos específicos de Microsoft

[[gsl::suppress(rules)]]

El atributo específico [[gsl::suppress(rules)]] de Microsoft se usa para suprimir advertencias de los comprobadores que aplican reglas de la Biblioteca de compatibilidad de directrices (GSL) en el código. Por ejemplo, considere este fragmento de código:

int main()
{
    int arr[10]; // GSL warning C26494 will be fired
    int* p = arr; // GSL warning C26485 will be fired
    [[gsl::suppress(bounds.1)]] // This attribute suppresses Bounds rule #1
    {
        int* q = p + 1; // GSL warning C26481 suppressed
        p = q--; // GSL warning C26481 suppressed
    }
}

En el ejemplo se generan estas advertencias:

  • C26494 (Regla de tipo 5: Inicializar siempre un objeto).

  • C26485 (Regla de límites 3: Sin matriz a decaimiento de puntero).

  • C26481 (Regla de límites 1: No use aritmética de punteros. Use span en su lugar).

Las dos primeras advertencias se activan al compilar este código con la herramienta de análisis de código CppCoreCheck instalada y activada. Pero la tercera advertencia no se desencadena debido al atributo. Puede suprimir todo el perfil de límites escribiendo [[gsl::suppress(bounds)]] sin incluir un número de regla específico. Las directrices de C++ Core Guidelines están diseñadas para ayudarle a escribir código mejor y más seguro. El atributo suppress facilita la desactivación de las advertencias cuando no se desean.

[[msvc::flatten]]

El atributo [[msvc::flatten]] específico de Microsoft es muy similar a [[msvc::forceinline_calls]]y se puede usar en los mismos lugares y de la misma manera. La diferencia es que [[msvc::flatten]] todas [[msvc::forceinline_calls]] las llamadas del ámbito a las que se aplica de forma recursiva, hasta que no quedan llamadas. Esto puede tener consecuencias para el crecimiento del tamaño de código resultante de la función o el rendimiento del compilador, que debe administrar manualmente.

[[msvc::forceinline]]

Cuando se coloca antes de una declaración de función, el atributo [[msvc::forceinline]] específico de Microsoft tiene el mismo significado que __forceinline.

[[msvc::forceinline_calls]]

El atributo [[msvc::forceinline_calls]] específico de Microsoft se puede colocar en o antes de una instrucción o un bloque. Hace que la heurística insertada intente todas [[msvc::forceinline]] las llamadas de esa instrucción o bloque:

void f() {
    [[msvc::forceinline_calls]]
    {
        foo();
        bar();
    }
    ...
    [[msvc::forceinline_calls]]
    bar();
    
    foo();
}

La primera llamada a fooy ambas llamadas a bar, se tratan como si se declararan __forceinline. La segunda llamada a foo no se trata como __forceinline.

[[msvc::intrinsic]]

El [[msvc::intrinsic]] atributo tiene tres restricciones en la función a la que se aplica:

  • La función no puede ser recursiva; su cuerpo solo debe tener una instrucción return con un static_cast del tipo de parámetro al tipo de valor devuelto.
  • La función solo puede aceptar un único parámetro.
  • La opción del compilador /permissive- no es necesaria. (Las /std:c++20 opciones y posteriores implican /permissive- de forma predeterminada).

El atributo específico [[msvc::intrinsic]] de Microsoft indica al compilador que inserte una metafunción que actúa como una conversión con nombre del tipo de parámetro al tipo de valor devuelto. Cuando el atributo está presente en una definición de función, el compilador reemplaza todas las llamadas a esa función por una conversión simple. El [[msvc::intrinsic]] atributo está disponible en visual Studio 2022, versión 17.5, versión preliminar 2 y versiones posteriores. Este atributo solo se aplica a la función específica que la sigue.

Ejemplo

En este código de ejemplo, el [[msvc::intrinsic]] atributo aplicado a la my_move función hace que el compilador reemplace las llamadas a la función por la conversión estática insertada en su cuerpo:

template <typename T>
[[msvc::intrinsic]] T&& my_move(T&& t) { return static_cast<T&&>(t); }

void f() {
    int i = 0;
    i = my_move(i);
}

[[msvc::noinline]]

Cuando se coloca antes de una declaración de función, el atributo [[msvc::noinline]] específico de Microsoft tiene el mismo significado que declspec(noinline).

[[msvc::noinline_calls]]

El atributo [[msvc::noinline_calls]] específico de Microsoft tiene el mismo uso que [[msvc::forceinline_calls]]. Se puede colocar antes de cualquier instrucción o bloque. En lugar de forzar la inserción de todas las llamadas en ese bloque, tiene el efecto de desactivar la inserción para el ámbito al que se aplica.

[[msvc::no_tls_guard]]

El atributo específico [[msvc::no_tls_guard]] de Microsoft deshabilita las comprobaciones de inicialización en el primer acceso a variables locales de subprocesos en archivos DLL. Las comprobaciones están habilitadas de forma predeterminada en el código compilado con Visual Studio 2019 versión 16.5 y versiones posteriores. Este atributo solo se aplica a la variable específica que la sigue. Para deshabilitar las comprobaciones globalmente, use la /Zc:tlsGuards- opción del compilador.