[[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