[[nodiscard]]
è solo un suggerimento
Come ha spiegato Andrew Tomazos in P0189R0
, [[nodiscard]]
è piuttosto un suggerimento del designer, non il requisito. Un utente potrebbe voler eliminare intenzionalmente i risultati della funzione e il designer non dovrebbe negare le sue invenzioni.
The existing practice demonstrates there are cases when the programmer intentionally wants to discard the
result of a nodiscard function, even though in most cases they do not. The existing nodiscard is a hint from
the function designer to the function user, that immediately destroying the result is most likely not what you
want, but it isn’t a straightjacket and isn’t used as such.
In seguito a questa dichiarazione, entrambi i casi in cui l'attributo [[nodiscard]]
potrebbe causare un errore o la parola chiave nodiscard
potrebbe causare un comportamento indefinito, sono stati attivati a favore dell'attributo [[nodiscard]]
causando solo un avviso.
Ulteriori [[nodiscard]]
usi
Oltre alla risposta di @ amon, come ha sottolineato Herb Sutter in Rapporto di viaggio: la riunione sugli standard ISO C ++ invernali , l'attributo [[nodiscard]]
ha due ulteriori utilizzi:
-
Annotare un tipo per applicare a tutti gli usi di quel tipo come valore restituito, come nel caso di
std::async
, dove ignorare il valore restituito significherebbe non essere affatto asincroni.
-
Indicazione di una differenza per prevenire l'errore , come nel caso in cui usi
empty()
e clear()
, quindi per esempio l'utente saprà che chiamare empty()
non ha effetti collaterali se chiamato per errore.
Canonical examples include the return value of std::async where ignoring the return value would mean being not async at all, and the return value of a container’s empty() function where people sometimes confuse empty() and clear() and we don’t want them to think calling empty() has side effects if they call it by mistake.
[[nodiscard]]
significato implicito per risorse che richiedono funzioni
- Si noti che la seguente sezione è correlata solo al compilatore di Visual Studio con
/analyze
o altrimenti abilitato C6031
warning.
La documentazione di Visual Studio su C6031
afferma che il significato di [[nodiscard]]
è implicito per risorse che richiedono funzioni, come disco, rete o memoria.
In general, it is not safe to assume that a call to function requiring disk, network, memory, or other resources will always succeed.
Risulta che il significato di [[nodiscard]]
è implicito per tali funzioni, e il chiamante dovrebbe sempre prestare attenzione al valore del risultato. Scartando il valore di tale funzione viene emesso anche un avviso C6031
se abilitato ( ie compilato con /analyze
).
Esempio
Nell'esempio seguente, l'eliminazione del std::fopen
risultato causerà anche un avviso di C6031
anche se std::fopen
non ha un attributo [[nodiscard]]
esplicito.
fopen("/._1", "r"); // C6031
FILE* Fp = fopen("/._2", "r"); // OK
The caller should always check the return value and handle error cases appropriately.
esteso [[nodiscard]]
: deve ispezionare il risultato
Visual Studio va ancora oltre, consentendo la funzione di marcatura con _Must_inspect_result_
che non solo disabilita il valore di scarto, ma costringe l'utente a controllarne il valore piuttosto che a farlo con void
.
Also consider using the _Must_inspect_result_
annotation, which checks that the value is examined in a useful way.
_Must_inspect_result_
valore di ritorno annotato deve essere più inspected o verrà generato un avviso.
"Inspection" includes whether it is used in a conditional expression, is assigned to an output parameter or global, or is passed as a parameter.
Esempio
Nell'esempio seguente, il risultato dell'eliminazione causerà l'avvertimento C6031
, ma l'assegnazione senza ispezione determinerà un avviso C28193
. Solo quando esamineremo definitivamente i risultati, non verrà emesso alcun avviso.
_Must_inspect_result_ bool f__Must_inspect_result_(){ return true; };
f__Must_inspect_result_(); // C6031
bool b2 = f__Must_inspect_result_(); // C28193
bool b3 = f__Must_inspect_result_(); if(b3); // OK
In some rare instances, the return value is intentionally not used. The most common of these instances is where a string length is returned but not actually used before some other test is made.
I miei pensieri: [[maybe_noinspect]]
Poiché è possibile differenziare due tipi di [[nodiscard]]
:
-
standard significato: avviso di rilascio su scartato risultato
-
esteso significato: avviso di rilascio su non ispezionato risultato
Chiederò una domanda retorica: perché non utilizzare entrambi gli attributi [[nodiscard]]
e [[maybe_noinspect]]
?
-
[[nodiscard]]
attributo potrebbe rimanere invariato, il che significa che il risultato non deve essere scartato, ma genera un avviso se il valore restituito non è rispettato, finché è presente l'attributo [[maybe_noinspect]]
.
-
[[maybe_noinspect]]
attributo potrebbe significare che il valore restituito potrebbe non essere rispettato, ignorando l'avviso causato dal valore non osservato, ma il valore non rispettato restituito con l'attributo [[nodiscard]]
.
[[maybe_noinspect]]
può essere utilizzato sia insieme a [[nodiscard]]
, sia da solo, possibilmente implicando l'attributo [[nodiscard]]
nel secondo caso.
Esempio
Se avessimo [[maybe_noinspect]]
e implicherebbe [[nodiscard]]
, potremmo estendere il significato di [[nodiscard]]
, con la possibilità di trasformarlo in standard significando indietro di [[maybe_noinspect]]
.
[[nodiscard]] bool f_nodiscard() { return true; };
[[maybe_noinspect]] bool f_maybe_noinspect(){ return true; };
f_nodiscard(); // Warning, value is discarded
bool b1 = f_nodiscard(); // Warning, value is uninspected
bool b2 = f_nodiscard(); if(b2); // OK, value is inspected
f_maybe_noinspect(); // Warning, value is discarded
bool b3 = f_maybe_noinspect(); // OK, value may be not inspected
bool b4 = f_maybe_noinspect(); if(b4); // OK, value is inspected