Szukaj…
Składnia
[[szczegóły]]: Prosty atrybut bez argumentu
[[szczegóły (argumenty)]]: Atrybut z argumentami
__attribute (szczegóły): niestandardowe specyficzne dla GCC / Clang / IBM
__declspec (szczegóły): Niestandardowy specyficzny dla MSVC
[[bez powrotu]]
C ++ 11 wprowadził atrybut [[noreturn]]
. Może być użyty dla funkcji, aby wskazać, że funkcja nie wraca do wywołującego przez wykonanie instrukcji return lub dotarcie do końca, jeśli jest treścią (ważne jest, aby pamiętać, że nie dotyczy to funkcji void
, ponieważ wracają do dzwoniącego, po prostu nie zwracają żadnej wartości). Taka funkcja może zakończyć się wywołaniem std::terminate
lub std::exit
lub zgłoszeniem wyjątku. Warto również zauważyć, że taka funkcja może powrócić, wykonując longjmp
.
Na przykład poniższa funkcja zawsze wyrzuca wyjątek lub wywołuje std::terminate
, więc jest dobrym kandydatem na [[noreturn]]
:
[[noreturn]] void ownAssertFailureHandler(std::string message) {
std::cerr << message << std::endl;
if (THROW_EXCEPTION_ON_ASSERT)
throw AssertException(std::move(message));
std::terminate();
}
Ten rodzaj funkcjonalności pozwala kompilatorowi zakończyć funkcję bez instrukcji return, jeśli wie, że kod nigdy nie zostanie wykonany. Tutaj, ponieważ wywołanie ownAssertFailureHandler
(zdefiniowane powyżej) w poniższym kodzie nigdy nie zwróci, kompilator nie musi dodawać kodu poniżej tego wywołania:
std::vector<int> createSequence(int end) {
if (end > 0) {
std::vector<int> sequence;
sequence.reserve(end+1);
for (int i = 0; i <= end; ++i)
sequence.push_back(i);
return sequence;
}
ownAssertFailureHandler("Negative number passed to createSequence()"s);
// return std::vector<int>{}; //< Not needed because of [[noreturn]]
}
Jeśli funkcja rzeczywiście zwróci, zachowanie jest niezdefiniowane, więc nie jest dozwolone:
[[noreturn]] void assertPositive(int number) {
if (number >= 0)
return;
else
ownAssertFailureHandler("Positive number expected"s); //< [[noreturn]]
}
Zauważ, że [[noreturn]]
jest najczęściej używany w funkcjach void. Nie jest to jednak wymagane, aby można było używać funkcji w programowaniu ogólnym:
template<class InconsistencyHandler>
double fortyTwoDivideBy(int i) {
if (i == 0)
i = InconsistencyHandler::correct(i);
return 42. / i;
}
struct InconsistencyThrower {
static [[noreturn]] int correct(int i) { ownAssertFailureHandler("Unknown inconsistency"s); }
}
struct InconsistencyChangeToOne {
static int correct(int i) { return 1; }
}
double fortyTwo = fortyTwoDivideBy<InconsistencyChangeToOne>(0);
double unreachable = fortyTwoDivideBy<InconsistencyThrower>(0);
Następujące standardowe funkcje biblioteczne mają ten atrybut:
- std :: abort
- std :: exit
- std :: quick_exit
- std :: nieoczekiwany
- std :: terminate
- std :: wyjątek_rethrow
- std :: throw_with_nested
- std :: nested_exception :: rethrow_nested
[[upadek]]
Za każdym razem, gdy case
zakończy się switch
, kod następnej sprawy zostanie wykonany. Tej ostatniej można zapobiec, używając instrukcji „break”. Ponieważ to tak zwane przewrotne zachowanie może wprowadzać błędy, gdy nie są zamierzone, kilka kompilatorów i analizatorów statycznych ostrzega o tym.
Począwszy od C ++ 17 wprowadzono standardowy atrybut wskazujący, że ostrzeżenie nie jest potrzebne, gdy kod ma ulec awarii. Kompilatory mogą bezpiecznie wydawać ostrzeżenia, gdy sprawa zakończy się bez break
lub [[fallthrough]]
i ma co najmniej jedno zdanie.
switch(input) {
case 2011:
case 2014:
case 2017:
std::cout << "Using modern C++" << std::endl;
[[fallthrough]]; // > No warning
case 1998:
case 2003:
standard = input;
}
Zobacz propozycję bardziej szczegółowych przykładów, w jaki sposób można zastosować [[fallthrough]]
.
[[przestarzałe]] i [[przestarzałe („powód”)]]
C ++ 14 wprowadził standardowy sposób wycofywania funkcji za pomocą atrybutów. [[deprecated]]
może być użyte do wskazania, że funkcja jest przestarzała. [[deprecated("reason")]]
pozwala na dodanie konkretnego powodu, który może być pokazany przez kompilator.
void function(std::unique_ptr<A> &&a);
// Provides specific message which helps other programmers fixing there code
[[deprecated("Use the variant with unique_ptr instead, this function will be removed in the next release")]]
void function(std::auto_ptr<A> a);
// No message, will result in generic warning if called.
[[deprecated]]
void function(A *a);
Ten atrybut można zastosować do:
- deklaracja klasy
- typedef-name
- zmienna
- element danych niestatycznych
- funkcja
- wyliczenie
- specjalizacja szablonów
(ref. c ++ 14 standardowy szkic : 7.6.5 Przestarzały atrybut)
[[nodiscard]]
[[nodiscard]]
może służyć do wskazania, że zwracana wartość funkcji nie powinna być ignorowana podczas wykonywania wywołania funkcji. Jeśli zwracana wartość zostanie zignorowana, kompilator powinien ostrzec o tym. Atrybut można dodać do:
- Definicja funkcji
- Typ
Dodanie atrybutu do typu działa tak samo, jak dodanie atrybutu do każdej funkcji, która zwraca ten typ.
template<typename Function>
[[nodiscard]] Finally<std::decay_t<Function>> onExit(Function &&f);
void f(int &i) {
assert(i == 0); // Just to make comments clear!
++i; // i == 1
auto exit1 = onExit([&i]{ --i; }); // Reduce by 1 on exiting f()
++i; // i == 2
onExit([&i]{ --i; }); // BUG: Reducing by 1 directly
// Compiler warning expected
std::cout << i << std::end; // Expected: 2, Real: 1
}
Zobacz propozycję bardziej szczegółowych przykładów [[nodiscard]]
.
Uwaga: Dane wdrożeniowe Finally
/ onExit
zostały pominięte w przykładzie, patrz Finally / ScopeExit .
[[może_nieużywany]]
[[maybe_unused]]
jest tworzony w celu wskazania w kodzie, że pewna logika może nie zostać użyta. Jest to często powiązane z warunkami przedprocesorowymi, w których można to wykorzystać lub nie. Ponieważ kompilatory mogą wyświetlać ostrzeżenia o nieużywanych zmiennych, jest to sposób na ich wyłączenie poprzez wskazanie zamiaru.
Typowym przykładem zmiennych, które są potrzebne w kompilacjach debugowania, gdy nie są potrzebne w produkcji, są zwracane wartości wskazujące powodzenie. W kompilacjach do debugowania warunek powinien być zapewniony, chociaż w produkcji zostały one usunięte.
[[maybe_unused]] auto mapInsertResult = configuration.emplace("LicenseInfo", stringifiedLicenseInfo);
assert(mapInsertResult.second); // We only get called during startup, so we can't be in the map
Bardziej złożonym przykładem są różne rodzaje funkcji pomocniczych, które znajdują się w nienazwanej przestrzeni nazw. Jeśli te funkcje nie są używane podczas kompilacji, kompilator może dać im ostrzeżenie. Najlepiej byłoby chronić je za pomocą tych samych tagów preprocesora, co program wywołujący, jednak ponieważ może to być skomplikowane, atrybut [[maybe_unused]]
jest łatwiejszą do utrzymania alternatywą.
namespace {
[[maybe_unused]] std::string createWindowsConfigFilePath(const std::string &relativePath);
// TODO: Reuse this on BSD, MAC ...
[[maybe_unused]] std::string createLinuxConfigFilePath(const std::string &relativePath);
}
std::string createConfigFilePath(const std::string &relativePath) {
#if OS == "WINDOWS"
return createWindowsConfigFilePath(relativePath);
#elif OS == "LINUX"
return createLinuxConfigFilePath(relativePath);
#else
#error "OS is not yet supported"
#endif
}
Zobacz propozycję bardziej szczegółowych przykładów [[maybe_unused]]
.