by Marc Mutz
This QUIP aims to document how we use the [[nodiscard]] attribute in
Qt.
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.
[[nodiscard]] is a C++17 addition, Qt 5 has the
Q_REQUIRED_RESULT macro, defined to platform-dependent equivalents,
if any. This macro can only be used on (non-constructor) functions.[[nodiscard]] unconditionally on
(non-constructor) functions and at class level (non-exported classes
only). The use of Q_REQUIRED_RESULT is discouraged in Qt 6.Q_NODISCARD_CTOR to apply [[nodiscard]]
to contructors, too.[[nodiscard("reason")]] via
Q_NODISCARD_X and Q_NODISCARD_CTOR_X.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.
Q_NODISCARD_CTOR. Constructors of other
classes MAY be marked as Q_NODISCARD_CTOR, at author’s and
reviewer’s discretion.[[nodiscard]] on constructors.from~~~~() or create~~~()) SHALL be marked [[nodiscard]].QList::empty(), which sounds like a modifying
function, but in fact is const) SHALL be marked as [[nodiscard]]
(or Q_REQUIRED_RESULT, in Qt 5).[[nodiscard]], at author’s and reviewer’s
discretion.[[nodiscard]], unless they fall into Case 1.[[nodiscard]] on whole classes. Qt still
contains such uses, but they are historic and scheduled to be
replaced with Q_NODISCARD_CTOR on 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.[[nodiscard]] classes cannot be exported
wholesale at the moment. If there is enough demand, this may be
worked around in the future.Syntactically, [[nodiscard]] MUST come first in the declaration,
esp. before a Q_*_EXPORT macro, 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]] or Q_NODISCARD_CTOR as
part of the line that also contains the (leading) return type.
See point 1 for examples.
When adding [[nodiscard]] or Q_NODISCARD_CTOR to 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 _X variants and [[nodiscard("reason")]] SHOULD always be
placed on a separate line, because they tend to be long themselves.