This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.

4268. function<void()> suppresses nodiscard warnings

Section: 22.10.4 [func.require] Status: New Submitter: Jonathan Wakely Opened: 2025-05-29 Last modified: 2025-05-29

Priority: Not Prioritized

View other active issues in [func.require].

View all other issues in [func.require].

View all issues with New status.

Discussion:

struct [[nodiscard]] A { };
A f();
std::function<void()> func = f;

Invoking func() will discard the return value of f(), but there will be no warning. This is because INVOKE<void>(...) is defined in terms of static_cast<void>(...) and the explicit cast to void suppresses nodiscard warnings. This is in contast to INVOKE<R>(...) where the conversion to non-void R is implicit.

It seems right that std::invoke_r<void>(f) should not give nodiscard warnings, because that's quite explicit about converting to void, and similarly for std::bind<void>(f)(). However, I think it's debatable whether all uses of INVOKE<void> (and std::function<void()> in particular) intend an explicit cast to void that ignores nodiscard types. It's very easy to set f as the target of func and then lose its warning, and there's no explicit use of void when you write func = f; func();.

We could consider defining INVOKE<void>(...) to be an expression of type void, without explicitly saying there's a cast to void. For example, (INVOKE(...), void()) would invoke the invocable and have type void, but would not require any nodiscard warnings to be suppressed. If we did that, some uses of INVOKE<R> such as std::invoke_r and std::bind<R> might need to be adjusted to preserve the explicit conversion to void. That would allow us to be selective about which uses of INVOKE<void> we consider to be explicit about discarding results, and which we don't.

Proposed resolution: