QUIP 19: [[nodiscard]] Policy
by Marc Mutz
Details
- Number: 19
- Title: [[nodiscard]] Policy
- Author: Marc Mutz
- Status: Active
- Type: Implementation
- Post History: 044044.html
- Created: 19 June 2023
This QUIP aims to document how we use the [[nodiscard]] attribute in
Qt.
Motivation
The [[nodiscard]] attribute can be applied to functions to cause a
warning if the function’s return value is ignored (discarded). This can
be used to alert the user of the function to non-sensical
(list.empty();) or dangerous code (ignoring error codes).
It can also be applied to constructors (but see below) to warn about the
object being ignored (QMutexLocker(&mutex) instead of
QMutexLocker locker(&mutex)).
Finally, it can be applied to classes whole-sale, in which case it applies to all functions returning that type, Qt or user functions, and, depending on compiler, to all constructors of the type, too.
Version History
- Since
[[nodiscard]]is a C++17 addition, Qt 5 has theQ_REQUIRED_RESULTmacro, defined to platform-dependent equivalents, if any. This macro can only be used on (non-constructor) functions. - Since Qt 6.0, we can use
[[nodiscard]]unconditionally on (non-constructor) functions and at class level (non-exported classes only). The use ofQ_REQUIRED_RESULTis discouraged in Qt 6. - Since Qt 6.6, we can use
Q_NODISCARD_CTORto apply[[nodiscard]]to contructors, too. - Since Qt 6.7, we have access to C++20
[[nodiscard("reason")]]viaQ_NODISCARD_XandQ_NODISCARD_CTOR_X.
Policy
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.
Unless mentioned otherwise, the following applies to both public and private Qt C++ APIs.
Unless mentioned otherwise, whenever [[nodiscard]] or
Q_NODISCARD_CTOR are mentioned below, it SHALL be read as including
Q_NODISCARD_X and Q_NODISCARD_CTOR_X respectively.
Constructors
- From Qt 6.6 onwards, all constructors of RAII and smart pointer
classes SHALL be marked as
Q_NODISCARD_CTOR. Constructors of other classes MAY be marked asQ_NODISCARD_CTOR, at author’s and reviewer’s discretion. - Qt versions prior to 6.6 lack the macro and therefore can’t use
[[nodiscard]]on constructors. - If (1) asks to mark all constructors of a class nodiscard, then
named constructors (static
functions returning an instance of the class; often prefixed
from~~~~()orcreate~~~()) SHALL be marked[[nodiscard]].
Other Functions
- Functions where calls that ignore the return type are most likely
wrong (e.g.
QList::empty(), which sounds like a modifying function, but in fact isconst) SHALL be marked as[[nodiscard]](orQ_REQUIRED_RESULT, in Qt 5). - Other functions whose only side-effect is to produce the return
value, esp. const member functions,
MAY be marked as
[[nodiscard]], at author’s and reviewer’s discretion. - Functions that have side-effects SHALL NOT be marked
[[nodiscard]], unless they fall into Case 1.
Whole Classes
- We currently do not use
[[nodiscard]]on whole classes. Qt still contains such uses, but they are historic and scheduled to be replaced withQ_NODISCARD_CTORon all the class’ constructors. [[nodiscard]]MAY be used on classes, though, provided the user documentation (in case of public API) or the commit message (in case of private API) give rationale for doing so.- Due to a bug in GCC,
[[nodiscard]]classes cannot be exported wholesale at the moment. If there is enough demand, this may be worked around in the future.
Formatting
-
Syntactically,
[[nodiscard]]MUST come first in the declaration, esp. before aQ_*_EXPORTmacro, if any. If the function is a template, the template-initalizer MUST come first. C++ enforces both, even though some compilers are known to accept some forms of non-standard formatting, too (e.g. GCC).Examples:
template <typname T> [[nodiscard]] int foo(const T&); [[nodiscard]] Q_CORE_EXPORT int foo(const QString&); [[nodiscard]] Q_CORE_EXPORT QVeryLongReturnTypeClassName bar(const QString&); Q_NODISCARD_CTOR explicit QFoo(const QBar &);Definitions that are not declarations SHALL NOT repeat the attribute.
-
New code SHOULD add the
[[nodiscard]]orQ_NODISCARD_CTORas part of the line that also contains the (leading) return type.See point 1 for examples.
-
When adding
[[nodiscard]]orQ_NODISCARD_CTORto declarations that pertain to already-released API, the attribute SHOULD be added on a separate line, or in a line with other attributes newly-added for the current release. This makes the header diff easier to read in API diffs at the cost of somewhat more vertical space needed for the declaration going forward. -
The
_Xvariants and[[nodiscard("reason")]]SHOULD always be placed on a separate line, because they tend to be long themselves.