c++c++20nodiscard

Why not apply [[nodiscard]] to every constructor?


Since C++20, [[nodiscard]] can be applied to constructors. http://wg21.link/p1771 has the example:

struct [[nodiscard]] my_scopeguard { /* ... */ };
struct my_unique {
  my_unique() = default;                                // does not acquire resource
  [[nodiscard]] my_unique(int fd) { /* ... */ }         // acquires resource
  ~my_unique() noexcept { /* ... */ }                   // releases resource, if any
  /* ... */
};
struct [[nodiscard]] error_info { /* ... */ };
error_info enable_missile_safety_mode();
void launch_missiles();
void test_missiles() {
  my_scopeguard();              // warning encouraged
  (void)my_scopeguard(),        // warning not encouraged, cast to void
    launch_missiles();          // comma operator, statement continues
  my_unique(42);                // warning encouraged
  my_unique();                  // warning not encouraged
  enable_missile_safety_mode(); // warning encouraged
  launch_missiles();
}
error_info &foo();
void f() { foo(); }             // warning not encouraged: not a nodiscard call, because neither
                                // the (reference) return type nor the function is declared nodiscard

Usually constructors have no side effects. So discarding the result is pointless. For example, discarding std::vector as below is pointless:

std::vector{1,0,1,0,1,1,0,0};

It would be useful if std::vector constructor is [[nodiscard]], so that the above code produced a warning.

Notable constructors that do have side effects are lock constructors, like unique_lock or lock_guard. But then those are good target to be marked as [[nodiscard]] as well, to avoid missed scope, like here:

std::lock_guard{Mutex};
InterThreadVariable = value; // ouch, not protected by mutex

It would be useful if std::lock_guard constructor is [[nodiscard]], so that the above code produced a warning.

Sure there's a case like return std::lock_guard{Mutex}, InterThreadVariable;. But it is rare enough to still have [[nodiscard]] guards, and to suppress them locally like return ((void)std::lock_guard{Mutex}, InterThreadVariable);

So, is there any case when a constructor should not be nodiscard?


Solution

  • An example from the pybind11 library: To wrap a C++-class for python, you do:

    PYBIND11_MODULE(example, m) {
        py::class_<MyClass>(m, "MyClass");  // <-- discarded.
    }