Michael Rizkalla
by Michael Rizkalla

Formal attributes definition according to the C++ standards is as follows:

"9.12.1 Attributes specify additional information for various source constructs such as types, variables, names, blocks, or translation units."

The compiler uses attributes to further optimise the code according to the information each attribute contain. In this topic, I will discuss some attributes which I think are important and supported by the C++ standards as well. It’s worth nothing that each individual compiler provides additional attributes which are not supported by the standard. For instance, the __declspec() modifier provides Microsoft specific attributes such as: dllimport, dllexport and novtable.

Attributes in the standard

The standard supports eight different attributes and alignment specifier. In this article, I am going to discuss four attributes which I believe they could be used frequently. Each attribute will be followed by a demonstration. These attributes are following the syntax: [[attr]] or [[using attr-namespace: attri-list]] where attri-list is comma-separated of zero or more sequence

1) No return attribute - [[noreturn]] since C++11

This attribute indicates that the function does not return to the caller, which implies that the decorated function is an infinite loop, exit/abort or a throwing function. Returning from a function that is decorated with this attribute results in an ill-formed program. Using the attribute allows the compiler to identify un-reachable code and optimize for the decorated function.

[[noreturn]] void throwFunc() { // non returning code}

void func() {
    // ...
    throwFunc();
    // Unreachable code ...
    // more code
    // ...
}

2) Fall through attribute - [[fallthrough]] since C++17

This is an interesting attribute, its purpose is to indicate that a fallthrough is intentional this no warnings are generated by the compiler. The attribute must only be used with switch statements and applied to null statements (i.e. empty statement). The program will be ill formed if the attribute is used where no subsequent cases exist.

void func(int n) {
    switch(n) {
        case 1:
            std::cout << "1\n";
            [[fallthrough]] // OK
        case 2:
            std::cout << "2 or less\n"; // Warning
        case 3:
            std::cout << "3 or less\n";
            [[fallthrough]] // ill-formed 
    }
}

3) No discard - [[nodiscard]] since C++17

This attribute can be used in two forms [[nodiscard]] and [[nodiscard(string-literal)]]. The attribute informs the compiler that it can issue warnings if a decorated function returned a discrded-value expression that is not casted to void.

[[nodicard]] int func1() {return 1;}
int func2() {return 1;}

void func() {
    func1(); // Ok
    func2(); // Warning, discarded value type
}

4) Likely / Unlikely - [[likely]] / [[unlikely]] since C++20

This attribute decorates branch statements to inform the compiler of which branch is likely to be executed, so that the compiler is capable of providing better optimizations. This feature has not been implemented so far in most compilers, so keep an eye for it.

char func(int i){
    switch(i) {
        [[unlikely]] case 1:
            return '1';
            break;
        [[likely]] case 0:
            return '0';
            break;
        [[unlikely]] default:
            return 'N';
    }
}
  • To read more, please check the following references:
  • cppreference