<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8">
<title>Using [[nodiscard]] should be Recommended Practice</title>
  <style type='text/css'>
  body {font-variant-ligatures: none;}
  p {text-align:justify}
  li {text-align:justify}
  blockquote.note, div.note
  {
          background-color:#E0E0E0;
          padding-left: 15px;
          padding-right: 15px;
          padding-top: 1px;
          padding-bottom: 1px;
  }
  p code {color:navy}
  ins p code {color:#00A000}
  p ins code {color:#00A000}
  p del code {color:#A00000}
  ins {color:#00A000}
  del {color:#A00000}
  table#boilerplate { border:0 }
  table#boilerplate td { padding-left: 2em }
  table.bordered, table.bordered th, table.bordered td {
    border: 1px solid;
    text-align: center;
  }
  ins.block {color:#00A000; text-decoration: none}
  del.block {color:#A00000; text-decoration: none}
  #hidedel:checked ~ * del, #hidedel:checked ~ * del * { display:none; visibility:hidden }
  </style>
<meta property="og:title" content="Using [[nodiscard]] should be Recommended Practice">
<meta property="og:type" content="website">
<meta property="og:image" content="https://isocpp.org/assets/images/cpp_logo.png">
<meta property="og:url" content="https://wg21.link/<tr><td>Document number</td><td>P3122R1</td></tr>">
</head><body>
<table id="boilerplate">
<tr><td>Document number</td><td>P3122R1</td></tr>
<tr><td>Date</td><td>2024-03-12</td></tr>
<tr><td>Project</td><td>Programming Language C++, Library Working Group</td></tr>
<tr><td>Reply-to</td><td>Jonathan Wakely &lt;cxx&#x40;kayari.org&gt;</td></tr>
</table><hr>
<h1>Using [[nodiscard]] should be Recommended Practice</h1>
<ul>
 <li>
 <ul>
  <li><a href="#Revision-History">Revision History</a></li>
  <li><a href="#Introduction">Introduction</a></li>
  <li><a href="#Rationale">Rationale</a></li>
  <li><a href="#Proposed-LEWG-Policy">Proposed LEWG Policy</a></li>
  <li><a href="#Proposed-Standard-Wording">Proposed Standard Wording</a></li>
  <li><a href="#Acknowledgments">Acknowledgments</a></li>
  <li><a href="#References">References</a></li>
 </ul>
 </li>
</ul>
<a name="Revision-History"></a>
<h2>Revision History</h2>

<ul>
<li>R1 adds more rationale, proposed LEWG policy wording, and an additional
bullet in the proposed standard wording to cover cases like <code>std::remove_if</code>.</li>
</ul>


<a name="Introduction"></a>
<h2>Introduction</h2>

<p>Instead of sprinkling <code>[[nodiscard]]</code> in hundreds of places in the library
clauses, there should be a single normative recommendation to use it wherever
it's appropriate. LEWG policy should be to avoid spending time discussing it.</p>

<p>This paper proposes a policy for LEWG and new wording for the standard.</p>

<a name="Rationale"></a>
<h2>Rationale</h2>

<p>It's not a good use of time to discuss adding <code>[[nodiscard]]</code> to individual
function declarations. That requires LEWG time, LWG time, WG21 votes,
editorial changes, and reviews to ensure those changes were correctly applied.
For example,
<a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2377r0.html" title="[[nodiscard]] in the Standard Library: Clause 23 Iterators Library">P2377R0 [[nodiscard]] in the Standard Library: Clause 23 Iterators Library</a>
only covers a single clause and is still nearly ten pages of wording.
And it has a mistake in the very first change it proposes to a declaration,
which would need to be reviewed and corrected.</p>

<p>What we want is for real implementations to give real warnings to users writing
real code. Whether the attribute is literally present on individual
declarations in the PDF standard is not important.</p>

<p><a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2377r0.html#why-this-is-worth-the-committees-time">P2377R0</a>
argued that it's a good use of committee time to add it to the library because
it reminds implementors to use the attribute and improve the quality of
their implementations. I feel that a single normative recommendation to use it
is a far better use of time than micro-managing how implementations declare
individual functions.</p>

<p>Implementers are best placed to decide on which functions to add the attribute
for their specific implementation. If a compiler has built-in warnings
for side-effect-free expressions, e.g. it can determine that <code>it == end;</code>
doesn't modify its arguments and so issues a warning, then there is no need
for that implementation to explicitly add the <code>[[nodiscard]]</code> attribute to
every inline <code>operator==</code> definition.
<strong>We should aim for good quality diagnostics,
not worry about how that is achieved.</strong></p>

<p>Implementors are also more able to respond promptly to changing needs or to
user feedback regarding nodiscard diagnostics. New compiler releases happen
more frequently than revisions of the C++ standard, and compiler vendors can
trial new nodiscard warnings under an optional setting to gather usage
experience. This is more flexible than spending WG21 time to discuss an
addition that is then enshrined in the standard for at least three years
until the next revision (or until more committee time is spent to revise it
via a defect report).</p>

<p>The wording below only talks about functions. The valid uses for <code>nodiscard</code>
on types are less common, and it might be worth discussing individual cases,
and adding the attribute to declarations in the standard if implementors
need extra guidance there. Alternatively, maybe blanket wording can be
produced that accurately describes the cases that should produce warnings.</p>

<p>The wording does not cover functions which return a pointer to allocated
storage, such as <code>operator new</code> and <code>std::allocator&lt;T&gt;::allocate</code>.
All such functions are marked <code>nodiscard</code> already. If the group prefers it,
we could remove those existing <code>nodiscard</code> attributes and add a new bullet like
"Functions that return a pointer to allocated storage. [<em>Example</em>:
<code>operator new</code> returns memory which could not be deallocated if discarded.
&mdash; <em>end example</em>]"</p>

<p>The wording does not cover functions that are considered likely to be confused
with another function, due to ambiguous meaning of English nouns vs verbs,
such as <code>std::vector&lt;T, A&gt;::empty()</code> which could be misunderstood to
empty the container (which is what the <code>clear</code> function does). It is not easy
to specify generic wording that clearly defines what might or might not be
confusing, and the particular example of <code>vector::clear</code> is already covered
because it's a pure function that is only called for its return value. The
more general policy already solves the problem, without a special case based
on potential confusion around the name.</p>

<a name="Proposed-LEWG-Policy"></a>
<h2>Proposed LEWG Policy</h2>

<blockquote><p><strong>Policy</strong>: Implementors are encourage to diagnose discarded-value expressions
via blanket wording in the library introduction clause, so the explicit use of
<code>nodiscard</code> in library wording should be avoided.
Subclause [lib.nodiscard] covers the cases that implementors are encouraged
to diagnose. Anything covered there does not need to be marked <code>nodiscard</code> in
library wording. New proposals should not use <code>nodiscard</code> in proposed wording
unless it's a case not covered by [lib.nodiscard], and then the paper should
provide rationale for not following the policy.<br/>
<strong>Rational</strong>: Implementers are best placed to decide on which functions to
add the attribute for their specific implementation. They do not need the
standard to specify where it must be used.<br/>
<strong>Policy paper</strong>: <a href="https://wg21.link/P3122">P3122</a></p></blockquote>

<a name="Proposed-Standard-Wording"></a>
<h2>Proposed Standard Wording</h2>

<p>This wording is relative to <a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/n4971.pdf" title="Working Draft - Programming Languages -- C++">N4971</a>.</p>

<p>Add a new subclause after 16.4.6.15 [lib.types.movedfrom]:</p>

<p><ins>
<strong>16.4.6.?? Discarded calls [lib.nodiscard]</strong>
</ins></p>

<p><ins>
Except where shown otherwise, it is unspecified which functions defined
in the C++ standard library are marked with a <code>nodiscard</code> attribute.
</ins></p>

<p><ins>
<em>Recommended practice</em>:
Implementations should issue a warning for potentially-evaluated
discarded-value expressions ([expr.context]) where the expression is
a call to any of the following:
</ins></p>

<ul>
<li><ins> <code>operator==</code> and <code>operator&lt;=&gt;</code> functions. </ins></li>
<li><ins> <code>const</code> member functions and explicit object member functions
that do not directly or indirectly modify objects accessible via their
arguments.<br/>
[<em>Example</em>:
<code>vector::empty() const</code>
and
<code>unordered_map::bucket_size(size_type) const</code>
do not modify any objects.
&mdash; <em>end example</em>]
</ins></li>
<li><ins> Non-const member functions that return pointers, references, or iterators,
but do not modify the object parameter or the object that the return value
refers to.<br/>
[<em>Example</em>:
<code>vector::begin()</code>
and
<code>set::find(const key_type&amp;)</code>
are not <code>const</code> so that they can return a mutable iterator,
but they do not modify the container or its elements.
<code>basic_string::operator[](size_type)</code> does not modify the
string or its contents.
&mdash; <em>end example</em>]<br/>
</ins></li>
<li><ins> Non-member functions that do not directly or indirectly modify objects
accessible via their arguments, other than potentially modifying their
non-reference arguments.<br/>
[<em>Example</em>:
<code>as_const(T&amp;)</code> behaves like a cast, it has no side effects.
<code>back_inserter(Container&amp;)</code>
takes a reference to a non-<code>const</code> object but does not modify it.
<code>ranges::next(I)</code> increments its by-value parameter,
which does not modify any other objects.
&mdash; <em>end example</em>]
</ins></li>
<li><ins> The function call operators of customization point objects
([customization.point.object]), unless the return type is <code>void</code>.<br/>
</ins></li>
<li><ins> Mutating sequence operations ([alg.copy]) which return an iterator
such that the position the iterator refers to depends on a predicate
or the content of the input range, not just on the size of the input range.
[<em>Example</em>:
<code>copy_if</code> returns an iterator pointing to the end of the result range,
which depends on how many elements of the input range match the predicate.
<code>remove</code> and <code>unique</code> return an iterator pointing to the end of the result
range, which depends on how many elements from the input range were not
included in the output range.
&mdash; <em>end example</em>]</li>
</ul>


<a name="Acknowledgments"></a>
<h2>Acknowledgments</h2>

<p>Thanks to Nicolai Josuttis, Hana Dusíková, and Christopher Di Bella
for the past work identifying where <code>nodiscard</code> is useful.</p>

<a name="References"></a>
<h2>References</h2>

<p><a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0600r1.pdf" title="[[nodiscard]] in the Library">P0600R1</a>, [[nodiscard]] in the Library, Nicolai Josuttis, 2017.<br/>
<a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2351r0.pdf" title="Mark all library static cast wrappers as `[[nodiscard]]`">P2351R0</a>, Mark all library static cast wrappers as <code>[[nodiscard]]</code>, Hana Dusíková, 2020.<br/>
<a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2377r0.html" title="[[nodiscard]] in the Standard Library: Clause 23 Iterators Library">P2377R0</a>, [[nodiscard]] in the Standard Library: Clause 23 Iterators Library, Christopher Di Bella, 2021.<br/>
<a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/n4971.pdf" title="Working Draft - Programming Languages -- C++">N4971</a>, Working Draft - Programming Languages -- C++, Thomas Köppe, 2023.</p>
</body></html>
