What is the rationale behind having [[nodiscard]] types?

Viewed 497

What are usecases in which it is beneficial to use a [[nodiscard]] type?

On types, [[nodiscard]] emits a warning if the return value of any function returning an instance of that type is omitted; (Citation from p0068r0):

If [[nodiscard]] is marked on a type, it makes it so that all functions that return that type are implicitly [[nodiscard]].

while a [[nodiscard]] constructor (c++2a) is very useful for classes which manage resources (e.g. unique_ptr) and nodiscard for functions are for instance useful for make_unique I cannot come up with an example where a nodiscard for a type is useful and I'm interested in cases where it is used.

1 Answers

Consider the type std::unique_lock<M>. It's an obvious candidate for marking the constructor (specifically the one that takes M&) nodiscard, as we would not want to write some code like this by accident:

std::unique_lock<M>(m);
// oops! we actually meant
// std::unique_lock<M> lck(m);

This falls under the "manages resources" category.

But this is also an example of a type that we would not want to have discarded if returned from a function:

std::unique_lock<M> getLockFor(Arg x) {
    std::unique_lock<M> result;
    // acquire some lock based on x
    return result;
}

{
    auto lck = getLockFor(arg1);  // ok
    // do some stuff that requires the lock to be held
}
{
    getLockFor(arg2);  // warning!
    // lock will NOT be held here!
}

To get the warning in this case, we would need to mark the type as nodiscard, not just the constructor.

I think, in fact, this example illustrates that perhaps most classes that manage resources should be nodiscard, as it is probably a bug when we call a function that returns to us control over a resource, merely to release that resource immediately by not using the return value.

Related