Attributi in C++

Lo standard C++ definisce un set comune di attributi. Consente anche ai fornitori del compilatore di definire i propri attributi all'interno di uno spazio dei nomi specifico del fornitore. Tuttavia, i compilatori sono necessari solo per riconoscere gli attributi definiti nello standard.

In alcuni casi, gli attributi standard si sovrappongono ai parametri specifici __declspec del compilatore. In Microsoft C++, è possibile usare l'attributo [[deprecated]] anziché usare __declspec(deprecated). L'attributo [[deprecated]] viene riconosciuto da qualsiasi compilatore conforme. Per tutti gli altri __declspec parametri, ad dllimport esempio e dllexport, finora non esiste alcun attributo equivalente, pertanto è necessario continuare a usare __declspec la sintassi. Gli attributi non influiscono sul sistema dei tipi e non modificano il significato di un programma. I compilatori ignorano i valori degli attributi che non riconoscono.

Visual Studio 2017 versione 15.3 e successive (Disponibile con /std:c++17 e versioni successive): nell'ambito di un elenco di attributi è possibile specificare lo spazio dei nomi per tutti i nomi con un unico using introducer:

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

Attributi standard C++

In C++11 gli attributi forniscono un modo standardizzato per annotare costrutti C++ (inclusi, ad esempio, classi, funzioni, variabili e blocchi) con informazioni aggiuntive. Gli attributi possono essere o meno specifici del fornitore. Un compilatore può usare queste informazioni per generare messaggi informativi o per applicare una logica speciale durante la compilazione del codice con attributi. Il compilatore ignora tutti gli attributi che non riconosce, il che significa che non è possibile definire attributi personalizzati usando questa sintassi. Gli attributi sono racchiusi tra parentesi quadre doppie:

[[deprecated]]
void Foo(int);

Gli attributi rappresentano un'alternativa standardizzata alle estensioni specifiche del fornitore, ad #pragma esempio direttive, __declspec() (Visual C++) o __attribute__ (GNU). Tuttavia, sarà comunque necessario usare i costrutti specifici del fornitore per la maggior parte degli scopi. Lo standard specifica attualmente gli attributi seguenti che un compilatore conforme deve riconoscere.

[[carries_dependency]]

L'attributo [[carries_dependency]] specifica che la funzione propaga l'ordinamento delle dipendenze dei dati per la sincronizzazione dei thread. L'attributo può essere applicato a uno o più parametri, per specificare che l'argomento passato contiene una dipendenza nel corpo della funzione. L'attributo può essere applicato alla funzione stessa, per specificare che il valore restituito contiene una dipendenza dalla funzione. Il compilatore può usare queste informazioni per generare codice più efficiente.

[[deprecated]]

Visual Studio 2015 e versioni successive: l'attributo [[deprecated]] specifica che una funzione non è destinata all'uso. In alternativa, potrebbe non esistere nelle versioni future di un'interfaccia di libreria. L'attributo [[deprecated]] può essere applicato alla dichiarazione di una classe, un typedef-name, una variabile, un membro dati non statico, una funzione, uno spazio dei nomi, un'enumerazione, un enumeratore o una specializzazione di modello. Il compilatore può usare questo attributo per generare un messaggio informativo quando il codice client tenta di chiamare la funzione. Quando il compilatore Microsoft C++ rileva l'uso di un [[deprecated]] elemento, genera l'avviso del compilatore C4996.

[[fallthrough]]

Visual Studio 2017 e versioni successive: (disponibile con /std:c++17 e versioni successive). L'attributo [[fallthrough]] può essere usato nel contesto delle switch istruzioni come hint per il compilatore (o chiunque legga il codice) previsto dal comportamento di fallthrough. Il compilatore Microsoft C++ attualmente non avvisa sul comportamento di fallthrough, quindi questo attributo non ha alcun effetto sul comportamento del compilatore.

[[likely]]

Visual Studio 2019 versione 16.6 e successive: (disponibile con /std:c++20 e versioni successive). L'attributo [[likely]] specifica un hint per il compilatore che il percorso del codice per l'etichetta o l'istruzione con attributi è più probabile che venga eseguito rispetto alle alternative. Nel compilatore Microsoft, l'attributo [[likely]] contrassegna i blocchi come "codice critico", che incrementa un punteggio di ottimizzazione interno. Il punteggio viene incrementato di più quando si ottimizza la velocità e non tanto quando si ottimizzano le dimensioni. Il punteggio netto influisce sulla probabilità di inlining, annullamento del ciclo e vettorizzazione delle ottimizzazioni. L'effetto di [[likely]] e [[unlikely]] è simile all'ottimizzazione guidata dal profilo, ma limitato nell'ambito all'unità di conversione corrente. L'ottimizzazione del riordinamento dei blocchi non è ancora implementata per questo attributo.

[[maybe_unused]]

Visual Studio 2017 versione 15.3 e successive: (disponibile con /std:c++17 e versioni successive). L'attributo [[maybe_unused]] specifica che una variabile, una funzione, una classe, un typedef, un membro dati non statico, un'enumerazione o una specializzazione del modello può essere intenzionalmente inutilizzata. Il compilatore non avvisa quando non viene usata un'entità contrassegnata [[maybe_unused]] . Un'entità dichiarata senza l'attributo può essere successivamente dichiarata nuovamente con l'attributo e viceversa. Un'entità viene considerata contrassegnata dopo la prima dichiarazione contrassegnata [[maybe_unused]] viene analizzata e per il resto dell'unità di conversione corrente.

[[nodiscard]]

Visual Studio 2017 versione 15.3 e successive: (disponibile con /std:c++17 e versioni successive). Specifica che il valore restituito di una funzione non deve essere rimosso. Genera l'avviso C4834, come illustrato in questo esempio:

[[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]]

L'attributo [[noreturn]] specifica che una funzione non restituisce mai. In altre parole, genera sempre un'eccezione o esce. Il compilatore può modificare le regole di compilazione per [[noreturn]] le entità.

[[unlikely]]

Visual Studio 2019 versione 16.6 e successive: (disponibile con /std:c++20 e versioni successive). L'attributo [[unlikely]] specifica un hint per il compilatore che il percorso del codice per l'etichetta o l'istruzione con attributi è meno probabile che venga eseguito rispetto alle alternative. Nel compilatore Microsoft l'attributo [[unlikely]] contrassegna i blocchi come "codice a freddo", che decrementa un punteggio di ottimizzazione interna. Il punteggio viene decrementato di più quando si ottimizzano le dimensioni e non tanto quando si ottimizza la velocità. Il punteggio netto influisce sulla probabilità di inlining, annullamento del ciclo e vettorizzazione delle ottimizzazioni. L'ottimizzazione del riordinamento dei blocchi non è ancora implementata per questo attributo.

Attributi specifici di Microsoft

[[gsl::suppress(rules)]]

L'attributo specifico di [[gsl::suppress(rules)]] Microsoft viene usato per eliminare gli avvisi dai controlli che applicano le regole GSL (Guidelines Support Library) nel codice. Si consideri ad esempio questo frammento di codice:

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
    }
}

L'esempio genera questi avvisi:

  • C26494 (regola di tipo 5: inizializzare sempre un oggetto).

  • C26485 (Regola limiti 3: nessuna matrice da impostare sul decadimento del puntatore).

  • C26481 (regola limiti 1: non usare l'aritmetica del puntatore. Usare invece span.

I primi due avvisi vengono attivati quando si compila questo codice con lo strumento di analisi del codice CppCoreCheck installato e attivato. Ma il terzo avviso non viene generato a causa dell'attributo . È possibile eliminare l'intero profilo dei limiti scrivendo [[gsl::suppress(bounds)]] senza includere un numero di regola specifico. Le linee guida di base di C++ sono progettate per semplificare la scrittura di codice migliore e più sicuro. L'attributo suppress semplifica la disattivazione degli avvisi quando non sono desiderati.

[[msvc::flatten]]

L'attributo [[msvc::flatten]] specifico di Microsoft è molto simile a [[msvc::forceinline_calls]]e può essere usato nelle stesse posizioni e nello stesso modo. La differenza è che [[msvc::flatten]][[msvc::forceinline_calls]] tutte le chiamate nell'ambito vengono applicate in modo ricorsivo, fino a quando non vengono lasciate chiamate. Ciò può avere conseguenze per l'aumento delle dimensioni del codice risultante della funzione o della velocità effettiva del compilatore, che è necessario gestire manualmente.

[[msvc::forceinline]]

Se posizionato prima di una dichiarazione di funzione, l'attributo [[msvc::forceinline]] specifico di Microsoft ha lo stesso significato di __forceinline.

[[msvc::forceinline_calls]]

L'attributo [[msvc::forceinline_calls]] specifico di Microsoft può essere inserito in o prima di un'istruzione o di un blocco. Fa sì che l'euristica inline tenti [[msvc::forceinline]] tutte le chiamate in tale istruzione o blocco:

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

La prima chiamata a e entrambe le chiamate a foobarvengono considerate come se fossero dichiarate__forceinline. La seconda chiamata a foo non viene considerata come __forceinline.

[[msvc::intrinsic]]

L'attributo [[msvc::intrinsic]] ha tre vincoli sulla funzione a cui viene applicato:

  • La funzione non può essere ricorsiva; Il corpo deve avere solo un'istruzione return con un static_cast oggetto dal tipo di parametro al tipo restituito.
  • La funzione può accettare solo un singolo parametro.
  • L'opzione /permissive- del compilatore è obbligatoria. Le /std:c++20 opzioni e successive implicano /permissive- per impostazione predefinita.

L'attributo specifico di [[msvc::intrinsic]] Microsoft indica al compilatore di inline una metafunzione che funge da cast denominato dal tipo di parametro al tipo restituito. Quando l'attributo è presente in una definizione di funzione, il compilatore sostituisce tutte le chiamate a tale funzione con un cast semplice. L'attributo [[msvc::intrinsic]] è disponibile in Visual Studio 2022 versione 17.5 preview 2 e versioni successive. Questo attributo si applica solo alla funzione specifica che la segue.

Esempio

In questo codice di esempio, l'attributo [[msvc::intrinsic]] applicato alla my_move funzione esegue la sostituzione delle chiamate al compilatore alla funzione con il cast statico inlined nel corpo:

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]]

Se posizionato prima di una dichiarazione di funzione, l'attributo [[msvc::noinline]] specifico di Microsoft ha lo stesso significato di declspec(noinline).

[[msvc::noinline_calls]]

L'attributo [[msvc::noinline_calls]] specifico di Microsoft ha lo stesso utilizzo di [[msvc::forceinline_calls]]. Può essere posizionato prima di qualsiasi istruzione o blocco. Anziché forzare l'inlining di tutte le chiamate in tale blocco, ha l'effetto di disattivare l'inlining per l'ambito a cui viene applicato.

[[msvc::no_tls_guard]]

L'attributo specifico di [[msvc::no_tls_guard]] Microsoft disabilita i controlli per l'inizializzazione al primo accesso alle variabili locali del thread nelle DLL. I controlli sono abilitati per impostazione predefinita nel codice compilato con Visual Studio 2019 versione 16.5 e versioni successive. Questo attributo si applica solo alla variabile specifica che la segue. Per disabilitare i controlli a livello globale, usare l'opzione del /Zc:tlsGuards- compilatore.