Attributes

[[noreturn]]

  • Function attribute

  • Compiler can know that control flow will not return to the caller

  • ⟶ warnings

  • ⟶ optimizations

#include <stdlib.h>

[[noreturn]] void terminate()
{
    exit(0);
}

int foo()
{
    terminate();
    return 42;                                         // <-- *could* be warned
}

int main()
{
    return foo();
}
  • GCC 13 does not say anything though

[[deprecated]]

[[deprecated]] void old_crap()
{
    // ...
}

[[deprecated("this is crap")]] void old_crap2()
{
    // ...
}

int main()
{
    old_crap();                                        // <-- warning: ‘void old_crap()’ is deprecated
    old_crap2();                                       // <-- warning: ‘void old_crap2()’ is deprecated: this is crap
    return 0;
}

[[fallthrough]]: Problem

  • GCC: -Wimplicit-fallthrough is highly recommended (also enabled by -Wextra)

  • Omitting break is usually a bug

  • Similar functionality available from other compilers too

  • (GCC?) observation: have to have a statement before falling through

#include <cstdlib>
#include <ctime>
#include <iostream>

int main()
{
    std::srand(std::time(nullptr));
    int number = std::rand();

    bool seen_zero = false;
    
    switch (number) {
        case 0:
            seen_zero = true;                          // <-- warning: this statement may fall through
        case 1:
            std::cout << "uh, this was unlikely\n";
            break;
    }

    if (seen_zero)
        std::cout << "uh, this was even more unlikely\n";

    return 0;
}
  • Sometimes break-less case is desired

  • ⟶ Want to selectively disable

[[fallthrough]]: Usage

#include <cstdlib>
#include <ctime>
#include <iostream>

int main()
{
    std::srand(std::time(nullptr));
    int number = std::rand();

    bool seen_zero = false;
    
    switch (number) {
        case 0:
            seen_zero = true;
            [[fallthrough]];                           // <-- suppress warning
        case 1:
            std::cout << "uh, this was unlikely\n";
            break;
    }

    if (seen_zero)
        std::cout << "uh, this was even more unlikely\n";

    return 0;
}

[[nodiscard]]: Problem

  • Compiler does not insist that function return values are respected

  • There might be warning options, but these are probably too loud (and unwanted in most cases)

bool might_fail()
{
    return false;
}

int main()
{
    might_fail();                                      // <-- potential error not handled
    return 0;                                          // <-- ... though exit OK
}

[[nodiscard]]: Solution

  • [[nodiscard]], best together with -Werror

  • ⟶ Usage has to be fixed

[[nodiscard]] bool might_fail()
{
    return false;
}

int main()
{
    might_fail();                                      // <-- warning: ignoring return value of ‘bool might_fail()’, declared with attribute ‘nodiscard’
    return 0;
}
  • ⟶ fix it

[[nodiscard]] bool might_fail()
{
    return false;
}

int main()
{
    bool ok = might_fail();
    return ok?0:1;
}

[[nodiscard]] On Entire Types

enum class [[nodiscard]] Error
{
    OK,
    FAILED,
    NONSENSE,
    // ...
};


Error might_fail()
{
    return Error::FAILED;
}

int main()
{
    Error e = might_fail();
    return (e == Error::OK)?0:1;
}

[[maybe_unused]]

  • Compiler warns about unused variables (-Wunused-variable, -Wall)

  • Hard to find a usecase where this is not wanted

  • I don’t believe there is one

int main()
{
    int unused_variable;
    return 0;
}
int main()
{
    [[maybe_unused]] int unused_variable;
    return 0;
}

[[likely]]

  • Help the compiler with branch optimization

  • Attention though (see here): there may be a performance penalty for misguessing

That said …

#include <string>

int main(int, const char* const* argv)
{
    int num = std::stoi(argv[1]);

    if (num < 42) [[likely]]
        return 0;
    else [[unlikely]]
        return 1;
}
#include <string>

int main(int, const char* const* argv)
{
    int num = std::stoi(argv[1]);

    switch (num) {
        case 1: [[fallthrough]];
        case 2: [[fallthrough]];
        case 3: [[fallthrough]];
        // ...
        // even though 1..41 fall through to 42, they aren't as
        // likely as 42
        // ...
        case 41: [[fallthrough]];

        [[likely]] case 42:
            return 0;
        default:
            return 1;
    }
}