Atrybuty w języku C++

Standard języka C++ definiuje wspólny zestaw atrybutów. Umożliwia również dostawcom kompilatora definiowanie własnych atrybutów w przestrzeni nazw specyficznej dla dostawcy. Jednak kompilatory są wymagane tylko do rozpoznawania atrybutów zdefiniowanych w standardzie.

W niektórych przypadkach atrybuty standardowe nakładają się na parametry specyficzne dla __declspec kompilatora. W języku Microsoft C++można użyć atrybutu [[deprecated]] zamiast .__declspec(deprecated) Atrybut [[deprecated]] jest rozpoznawany przez dowolny zgodny kompilator. W przypadku wszystkich innych __declspec parametrów, takich jak dllimport i dllexport, do tej pory nie ma odpowiednika atrybutu, więc należy nadal używać __declspec składni. Atrybuty nie mają wpływu na system typów i nie zmieniają znaczenia programu. Kompilatory ignorują wartości atrybutów, których nie rozpoznają.

Program Visual Studio 2017 w wersji 15.3 lub nowszej (dostępny z i nowszymi wersjami /std:c++17 ): W zakresie listy atrybutów można określić przestrzeń nazw dla wszystkich nazw z jednym using wprowadzeniem:

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

Atrybuty standardowe języka C++

W języku C++11 atrybuty zapewniają ustandaryzowany sposób dodawania adnotacji do konstrukcji języka C++ (w tym między innymi klas, funkcji, zmiennych i bloków) z dodatkowymi informacjami. Atrybuty mogą być lub nie są specyficzne dla dostawcy. Kompilator może używać tych informacji do generowania komunikatów informacyjnych lub stosowania specjalnej logiki podczas kompilowania kodu przypisanego. Kompilator ignoruje wszystkie atrybuty, których nie rozpoznaje, co oznacza, że nie można zdefiniować własnych atrybutów niestandardowych przy użyciu tej składni. Atrybuty są ujęte w podwójne nawiasy kwadratowe:

[[deprecated]]
void Foo(int);

Atrybuty reprezentują ustandaryzowaną alternatywę dla rozszerzeń specyficznych dla dostawcy, __declspec() takich jak #pragma dyrektywy (Visual C++) lub __attribute__ (GNU). Jednak w większości celów nadal trzeba będzie używać konstrukcji specyficznych dla dostawcy. Obecnie standard określa następujące atrybuty, które powinien rozpoznać zgodny kompilator.

[[carries_dependency]]

Atrybut [[carries_dependency]] określa, że funkcja propaguje kolejność zależności danych na potrzeby synchronizacji wątków. Atrybut można zastosować do co najmniej jednego parametru, aby określić, że przekazany argument niesie zależność z treścią funkcji. Atrybut można zastosować do samej funkcji, aby określić, że wartość zwracana prowadzi zależność z funkcji. Kompilator może użyć tych informacji do wygenerowania bardziej wydajnego kodu.

[[deprecated]]

Program Visual Studio 2015 lub nowszy:[[deprecated]] atrybut określa, że funkcja nie jest przeznaczona do użycia. Lub, że może nie istnieć w przyszłych wersjach interfejsu biblioteki. Atrybut [[deprecated]] można zastosować do deklaracji klasy, nazwy definicji typu, zmiennej, niestatycznej składowej danych, funkcji, przestrzeni nazw, wyliczenia, wyliczania, wyliczania lub specjalizacji szablonu. Kompilator może użyć tego atrybutu do wygenerowania komunikatu informacyjnego, gdy kod klienta próbuje wywołać funkcję. Gdy kompilator języka Microsoft C++ wykryje użycie [[deprecated]] elementu, zgłasza ostrzeżenie kompilatora C4996.

[[fallthrough]]

Program Visual Studio 2017 i nowsze wersje: (dostępne w /std:c++17 wersjach i nowszych). Atrybut [[fallthrough]] może być używany w kontekście instrukcji switch jako wskazówki dla kompilatora (lub każdego, kto odczytuje kod), że zachowanie fallthrough jest zamierzone. Kompilator języka Microsoft C++ obecnie nie ostrzega przed zachowaniem fallthrough, więc ten atrybut nie ma wpływu na zachowanie kompilatora.

[[likely]]

Program Visual Studio 2019 w wersji 16.6 lub nowszej: (dostępne w wersjach i /std:c++20 nowszych). Atrybut [[likely]] określa wskazówkę dla kompilatora, że ścieżka kodu dla przypisanej etykiety lub instrukcji jest bardziej prawdopodobne do wykonania niż alternatywy. W kompilatorze [[likely]] firmy Microsoft atrybut oznacza bloki jako "gorący kod", co zwiększa wewnętrzny wynik optymalizacji. Wynik jest zwiększany w przypadku optymalizacji pod kątem szybkości, a nie tyle podczas optymalizowania rozmiaru. Wynik netto ma wpływ na prawdopodobieństwo inliningu, wyrejestrowania pętli i optymalizacji wektoryzacji. Efekt [[likely]] i [[unlikely]] jest podobny do optymalizacji sterowanej profilem, ale ograniczony w zakresie do bieżącej jednostki tłumaczenia. Optymalizacja zmiany kolejności bloków nie jest jeszcze zaimplementowana dla tego atrybutu.

[[maybe_unused]]

Program Visual Studio 2017 w wersji 15.3 lub nowszej: (dostępne w wersjach i /std:c++17 nowszych). Atrybut [[maybe_unused]] określa, że zmienna, funkcja, klasa, typedef, niestatyczna składowa danych, wyliczenia lub specjalizacja szablonu może być celowo nieużywana. Kompilator nie ostrzega, gdy nie jest używana jednostka oznaczona [[maybe_unused]] . Jednostkę zadeklarowaną bez atrybutu można później ponownie zadeklarować za pomocą atrybutu i odwrotnie. Jednostka jest uznawana za oznaczoną po pierwszej deklaracji oznaczonej [[maybe_unused]] jako analizowana, a w pozostałej części bieżącej lekcji tłumaczenia.

[[nodiscard]]

Program Visual Studio 2017 w wersji 15.3 lub nowszej: (dostępne w wersjach i /std:c++17 nowszych). Określa, że wartość zwracana funkcji nie jest przeznaczona do odrzucenia. Zgłasza ostrzeżenie C4834, jak pokazano w tym przykładzie:

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

Atrybut [[noreturn]] określa, że funkcja nigdy nie zwraca; innymi słowy, zawsze zgłasza wyjątek lub kończy działanie. Kompilator może dostosować reguły kompilacji dla [[noreturn]] jednostek.

[[unlikely]]

Program Visual Studio 2019 w wersji 16.6 lub nowszej: (dostępne w wersjach i /std:c++20 nowszych). Atrybut [[unlikely]] określa wskazówkę dla kompilatora, że ścieżka kodu dla przypisanej etykiety lub instrukcji jest mniej prawdopodobna do wykonania niż alternatywy. W kompilatorze [[unlikely]] firmy Microsoft atrybut oznacza bloki jako "zimny kod", co powoduje dekrementowanie wewnętrznego wyniku optymalizacji. Wynik jest dekrementowany bardziej podczas optymalizowania rozmiaru, a nie tyle podczas optymalizowania pod kątem szybkości. Wynik netto ma wpływ na prawdopodobieństwo inliningu, wyrejestrowania pętli i optymalizacji wektoryzacji. Optymalizacja zmiany kolejności bloków nie jest jeszcze zaimplementowana dla tego atrybutu.

Atrybuty specyficzne dla firmy Microsoft

[[gsl::suppress(rules)]]

Atrybut specyficzny dla firmy [[gsl::suppress(rules)]] Microsoft jest używany do pomijania ostrzeżeń z kontrolerów, które wymuszają reguły biblioteki pomocy technicznej wytycznych (GSL) w kodzie. Rozważmy na przykład ten fragment kodu:

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

W przykładzie są wyświetlane następujące ostrzeżenia:

  • C26494 (Reguła typu 5: Zawsze inicjuj obiekt).

  • C26485 (Reguła granic 3: brak tablicy do rozkładu wskaźnika).

  • C26481 (Reguła granic 1: Nie używaj arytmetyki wskaźnika. Zamiast tego użyj zakresu).

Pierwsze dwa ostrzeżenia są uruchamiane podczas kompilowania tego kodu za pomocą zainstalowanego i aktywowanego narzędzia do analizy kodu CppCoreCheck. Jednak trzecie ostrzeżenie nie jest uruchamiane z powodu atrybutu. Cały profil granic można pominąć, pisząc [[gsl::suppress(bounds)]] bez uwzględniania określonego numeru reguły. Podstawowe wytyczne języka C++ zostały zaprojektowane tak, aby ułatwić pisanie lepszego i bezpieczniejszego kodu. Atrybut pomijania ułatwia wyłączenie ostrzeżeń, gdy nie są one pożądane.

[[msvc::flatten]]

Atrybut specyficzny dla firmy Microsoft jest bardzo podobny do [[msvc::forceinline_calls]], [[msvc::flatten]] i może być używany w tych samych miejscach i w ten sam sposób. Różnica polega na tym, że [[msvc::flatten]][[msvc::forceinline_calls]] wszystkie wywołania w zakresie, do którego są stosowane rekursywnie, dopóki nie zostaną pozostawione żadne wywołania. Może to mieć konsekwencje dla wynikowego wzrostu rozmiaru kodu funkcji lub przepływności kompilatora, którym należy zarządzać ręcznie.

[[msvc::forceinline]]

Po umieszczeniu przed deklaracją funkcji atrybut [[msvc::forceinline]] specyficzny dla firmy Microsoft ma takie samo znaczenie jak __forceinline.

[[msvc::forceinline_calls]]

Atrybut [[msvc::forceinline_calls]] specyficzny dla firmy Microsoft można umieścić na instrukcji lub przed instrukcją lub blokiem. Powoduje to, że heurystyka śródliniowa próbuje podjąć [[msvc::forceinline]] wszystkie wywołania w tym oświadczeniu lub bloku:

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

Pierwsze wywołanie metody fooi oba wywołania metody barsą traktowane tak, jakby zostały zadeklarowane __forceinline. Drugie wywołanie foo metody nie jest traktowane jako __forceinline.

[[msvc::intrinsic]]

Atrybut [[msvc::intrinsic]] ma trzy ograniczenia dotyczące funkcji, do których ma zastosowanie:

  • Funkcja nie może być rekursywna; jego treść musi mieć tylko instrukcję return z typem static_cast parametru do zwracanego typu.
  • Funkcja może akceptować tylko jeden parametr.
  • Wymagana jest opcja kompilatora /permissive- . (Opcje /std:c++20 i późniejsze oznaczają /permissive- domyślnie).

Atrybut specyficzny dla [[msvc::intrinsic]] firmy Microsoft nakazuje kompilatorowi wbudowane działanie metafunction, które działa jako nazwane rzutowanie z typu parametru na typ zwracany. Gdy atrybut jest obecny w definicji funkcji, kompilator zamienia wszystkie wywołania tej funkcji na prostą rzutowanie. Atrybut [[msvc::intrinsic]] jest dostępny w programie Visual Studio 2022 w wersji 17.5 (wersja zapoznawcza 2) i nowszych wersjach. Ten atrybut ma zastosowanie tylko do określonej funkcji, która jest po niej zgodna.

Przykład

W tym przykładowym kodzie atrybut zastosowany do my_move funkcji sprawia, [[msvc::intrinsic]] że kompilator zamienia wywołania funkcji na wbudowany statyczny rzut w jego treści:

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

Po umieszczeniu przed deklaracją funkcji atrybut [[msvc::noinline]] specyficzny dla firmy Microsoft ma takie samo znaczenie jak declspec(noinline).

[[msvc::noinline_calls]]

Atrybut [[msvc::noinline_calls]] specyficzny dla firmy Microsoft ma takie samo użycie jak [[msvc::forceinline_calls]]. Można go umieścić przed dowolną instrukcją lub blokiem. Zamiast wymusić podkreślenie wszystkich wywołań w tym bloku, ma on wpływ na wyłączenie oznaczania zakresu, do którego jest stosowany.

[[msvc::no_tls_guard]]

Atrybut specyficzny dla [[msvc::no_tls_guard]] firmy Microsoft wyłącza sprawdzanie inicjowania pierwszego dostępu do zmiennych lokalnych wątku w bibliotekach DLL. Kontrole są domyślnie włączone w kodzie utworzonym przy użyciu programu Visual Studio 2019 w wersji 16.5 lub nowszej. Ten atrybut ma zastosowanie tylko do określonej zmiennej, która jest po niej zgodna. Aby wyłączyć kontrole globalnie, użyj opcji kompilatora /Zc:tlsGuards- .