Back to TILs

C++ attributes

Date: 2022-12-18Last modified: 2024-02-26

Table of contents

Brief

Attribute [[nodiscard]]

Function

[[nodiscard]] int get_zero() { return 0; }

[[nodiscard("Some custom message")]] int get_one() { return 1; }

[[nodiscard("XXX find me XXX on warning messages")]] int unused_function() {
  return -1;
}

void testFunction() {
  fmt::print(stderr, "testFunction()\n");
  get_zero();
  get_one();
}

Types

struct [[nodiscard]] ErrorType {};
ErrorType get_two() { return {}; }

void testType() {
  fmt::print(stderr, "testType()\n");
  get_two();
}

Lambda

void testLambda() {
  fmt::print(stderr, "testLambda()\n");
  auto lm = [] [[nodiscard]] () -> int { return 42; };
  lm();  // would emit a warning
}

Constructor

struct FDHolder {
  [[nodiscard]] FDHolder(int FD){};
  FDHolder(){};
};

void testConstructor() {
  fmt::print(stderr, "testConstructor()\n");
  FDHolder{42};    // warning
  FDHolder h{42};  // constructed object not discarded, no warning
  FDHolder{};      // defalt constructed, no warning
}

noexcept

noexcept notifies the user (and compiler) that a function may not throw an exception. If an exception is thrown from that function, terminate MUST be called.

void testNoExcept() noexcept {
  fmt::print(stderr, "testNoExcept()\n");
  // required to terminate the program
  throw 42;
}
  try {
    testFunction();
    testLambda();
    testConstructor();
    testNoExcept();  // terminate is called here
    testFunction();  // never called
  } catch (...) {
    // catch is irrelevant, `terminate` is called
  }
  fmt::print(stderr, "End of program\n");

Error output

testFunction()
testLambda()
testConstructor()
testNoExcept()
terminate called after throwing an instance of 'int'

Compilation output

attributes.cpp:62:3: warning: ignoring return value of function declared with 'nodiscard' attribute [-Wunused-result]
   62 |   get_zero();
      |   ^~~~~~~~
attributes.cpp:63:3: warning: ignoring return value of function declared with 'nodiscard' attribute: Some custom message [-Wunused-result]
   63 |   get_one();
      |   ^~~~~~~
attributes.cpp:75:3: warning: ignoring return value of function declared with 'nodiscard' attribute [-Wunused-result]
   75 |   get_two();
      |   ^~~~~~~
attributes.cpp:84:16: warning: an attribute specifier sequence in this position is a C++23 extension [-Wc++23-lambda-attributes]
   84 |   auto lm = [] [[nodiscard]] () -> int { return 42; };
      |                ^
attributes.cpp:85:5: warning: ignoring return value of function declared with 'nodiscard' attribute [-Wunused-result]
   85 |   lm();  // would emit a warning
      |   ~~^~
attributes.cpp:93:30: warning: unused parameter 'FD' [-Wunused-parameter]
   93 |   [[nodiscard]] FDHolder(int FD){};
      |                              ^
attributes.cpp:99:3: warning: ignoring temporary created by a constructor declared with 'nodiscard' attribute [-Wunused-value]
   99 |   FDHolder{42};    // warning
      |   ^~~~~~~~~~~~
attributes.cpp:115:3: warning: 'testNoExcept' has a non-throwing exception specification but can still throw [-Wexceptions]
  115 |   throw 42;
      |   ^
attributes.cpp:112:6: note: function declared non-throwing here
  112 | void testNoExcept() noexcept {
      |      ^              ~~~~~~~~
8 warnings generated.

References