Pourquoi convertir les valeurs de retour inutilisées en void?

112
int fn();

void whatever()
{
    (void) fn();
}

Y a-t-il une raison de convertir une valeur de retour inutilisée en annulation, ou ai-je raison de penser que c'est une perte de temps totale?

Suivre:

Eh bien, cela semble assez complet. Je suppose que c'est mieux que de commenter une valeur de retour inutilisée car le code auto-documenté est meilleur que les commentaires. Personnellement, je désactiverai ces avertissements car il s'agit d'un bruit inutile.

Je vais manger mes mots si un insecte s'échappe à cause de cela ...

markh44
la source

Réponses:

79

La réponse de David couvre à peu près la motivation pour cela, pour montrer explicitement aux autres "développeurs" que vous savez que cette fonction retourne mais que vous l'ignorez explicitement.

C'est un moyen de garantir que, là où c'est nécessaire, les codes d'erreur sont toujours traités.

Je pense que pour C ++, c'est probablement le seul endroit où je préfère également utiliser des casts de style C, car utiliser la notation de cast statique complète me semble exagéré ici. Enfin, si vous passez en revue une norme de codage ou en écrivez une, il est également judicieux d'indiquer explicitement que les appels à des opérateurs surchargés (n'utilisant pas la notation d'appel de fonction) devraient également en être exemptés:

class A {};
A operator+(A const &, A const &);

int main () {
  A a;
  a + a;                 // Not a problem
  (void)operator+(a,a);  // Using function call notation - so add the cast.
Richard Corden
la source
c'est le seul endroit où je préfère le casting de style C. tho dans ma norme de codage, j'ajouterais (void) à "a + a;" trop (correctement paren, bien sûr: p)
Johannes Schaub - litb
8
Un peu hors sujet, mais pourquoi feriez-vous "a + a;" comme ça sans utiliser la valeur de retour? Cela me semble vraiment être un abus d'effets secondaires et obscurcit l'intention du code.
Rob K
5
Eh bien, celui que vous utiliserez tous les jours sera «a = a». Cela retourne l'objet assigné à (pour toutes les classes qui se comportent bien c'est-à-dire! :)). Un autre exemple est: os << "Hello World" << std :: endl. Chacun d'eux renvoie l'objet "os".
Richard Corden
46

Au travail, nous utilisons cela pour reconnaître que la fonction a une valeur de retour, mais le développeur a affirmé qu'il était prudent de l'ignorer. Puisque vous avez marqué la question comme C ++, vous devriez utiliser static_cast :

static_cast<void>(fn());

En ce qui concerne le compilateur, la conversion de la valeur de retour en void n'a pas beaucoup de sens.

David Holm
la source
Supprime-t-il l'avertissement concernant les valeurs de retour inutilisées?
Paul Tomblin
Non. @Mykola: Avec GCC4, vous pouvez attacher un attribut indiquant que la valeur de retour ne doit pas être ignorée, ce qui déclenchera un avertissement.
David Holm
2
J'utilise g ++ et il donne un avertissement même avec une telle distribution.
klew
2
quelle option d'avertissement utilisez-vous? -Wunused-value ne déclenche pas l'avertissement dans mon environnement
Mykola Golubyev
Dans VC ++, il supprime l'avertissement
jalf
39

La vraie raison de faire cela remonte à un outil utilisé sur le code C, appelé lint .

Il analyse le code à la recherche de problèmes possibles et émet des avertissements et des suggestions. Si une fonction renvoyait une valeur qui n'a pas été vérifiée, lintcela avertirait si cela était accidentel. Pour faire taire lintcet avertissement, vous lancez l'appel à (void).

Daniel Earwicker
la source
2
Cela a peut-être commencé de cette façon, mais la plupart des outils ont maintenant d'autres mécanismes pour supprimer les avertissements comme celui-ci. Aussi - indépendamment de la raison pour laquelle cela a commencé, dans le domaine du code critique de sécurité en particulier, c'est devenu le moyen habituel de «documenter» les intentions des développeurs.
Richard Corden
5
GCC donne un avertissement pour cela avec -Wall.
greyfade
23

La conversion en voidest utilisée pour supprimer les avertissements du compilateur pour les variables inutilisées et les valeurs ou expressions de retour non enregistrées.

La norme (2003) dit au §5.2.9 / 4,

Toute expression peut être explicitement convertie en type «cv void». La valeur de l'expression est ignorée .

Vous pouvez donc écrire:

//suppressing unused variable warnings
static_cast<void>(unusedVar);
static_cast<const void>(unusedVar);
static_cast<volatile void>(unusedVar);

//suppressing return value warnings
static_cast<void>(fn());
static_cast<const void>(fn());
static_cast<volatile void>(fn());

//suppressing unsaved expressions
static_cast<void>(a + b * 10);
static_cast<const void>( x &&y || z);
static_cast<volatile void>( m | n + fn());

Tous les formulaires sont valides. Je le raccourcis généralement comme:

//suppressing  expressions
(void)(unusedVar);
(void)(fn());
(void)(x &&y || z);

C'est aussi bien.

Nawaz
la source
1
Sauf que cela ne désactive pas toujours les avertissements du compilateur. gcc ne semble pas répondre au casting pour annuler
Matt
@Matt: Quelle version de GCC utilisez-vous? Pouvez-vous publier votre code sur ideone.com ou stacked-crooked.com (ce dernier est mieux).
Nawaz
1
Le libellé standard «jeté» implique-t-il que l'avertissement ne doit pas être soulevé par des linters?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
2
@CiroSantilli 巴拿馬 文件 六四 事件 法轮功: .... c'est une question intéressante. Actuellement, je ne connais pas la réponse à cela. Veuillez partager avec moi si vous le savez de toute façon.
Nawaz
8

Depuis c ++ 17, nous avons l' [[maybe_unused]]attribut qui peut être utilisé à la place du voidcast.

Florestan
la source
1
C ++ 17 a également ajouté nodiscardce qui peut être intéressant: stackoverflow.com/a/61675726/895245 Aussi je pense que vous ne pouvez pas appliquer [[maybe_unused]]directement à l'appel il semble, seulement à une variable factice qui accepte le retour de l'appel? Si c'est correct, c'est un peu bancal.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
4

Cast to void est gratuit. Ce ne sont que des informations pour le compilateur sur la façon de le traiter.

klew
la source
1
Il est "sans coût" si le temps de l'écrire et le temps ultérieur de lecture, de compréhension (puis d'ignorance) du code sont gratuits. OMI, l'écriture (void)me coûte du temps et de l'énergie et je me demande si l'auteur savait comment fonctionnent les expressions.
wallyk
4

Pour la fonctionnalité de votre programme, le casting pour annuler n'a pas de sens. Je dirais également que vous ne devriez pas l'utiliser pour signaler quelque chose à la personne qui lit le code, comme suggéré dans la réponse de David. Si vous souhaitez communiquer quelque chose sur vos intentions, il est préférable d'utiliser un commentaire. Ajouter un casting comme celui-ci ne semblera qu'étrange et soulèvera des questions sur la raison possible. Juste mon avis...

Dani van der Meer
la source
2
C'est un modèle connu pour dire explicitement aux autres que vous ne vous souciez pas de la valeur de retour, que vous n'avez pas simplement oublié de la gérer.
Spidey
For the functionality of you program casting to void is meaninglessPour l'auto-documentation et le nombre d'avertissements lors de la compilation, ce n'est vraiment pas le cas. you should not use it to signal something to the person that is reading the code [...] it is better to use a comment.Le meilleur commentaire est pas de commentaire , lorsque le code s'explique. Et, encore une fois, garde un compilateur d'avertissement valide heureux dans le processus.
underscore_d
"Ajouter un casting comme celui-ci ne fera qu'étrange et soulèvera des questions sur la raison possible". J'en ai découvert un, et c'est pourquoi je lis ceci. Cela ressemblait à de mystérieux abus de pile pour mon cerveau non entraîné.
mynameisnafe
1

De plus, lors de la vérification de la conformité de votre code aux normes MISTA (ou autres), les outils automatiques tels que LDRA ne vous permettront pas d'appeler une fonction qui a un type de retour sans qu'elle renvoie une valeur à moins que vous ne convertissiez explicitement la valeur retournée en (void)

Colin
la source
0

C ++ 17 [[nodiscard]]

C ++ 17 a normalisé la "valeur de retour entreprise ignorée" avec un attribut.

Par conséquent, j'espère que les implémentations conformes avertiront toujours seulement quand nodiscardest donné, et jamais avertiront autrement.

Exemple:

main.cpp

[[nodiscard]] int f() {
    return 1;
}

int main() {
    f();
}

compiler:

g++ -std=c++17 -ggdb3 -O0 -Wall -Wextra -pedantic -o main.out main.cpp

résultat:

main.cpp: In function int main()’:
main.cpp:6:6: warning: ignoring return value of int f()’, declared with attribute nodiscard [-Wunused-result]
    6 |     f();
      |     ~^~
main.cpp:1:19: note: declared here
    1 | [[nodiscard]] int f() {
      | 

Les éléments suivants évitent tous l'avertissement:

(void)f();
[[maybe_unused]] int i = f();

Je n'ai pas pu utiliser maybe_unuseddirectement sur l' f()appel:

[[maybe_unused]] f();

donne:

main.cpp: In function int main()’:
main.cpp:6:5: warning: attributes at the beginning of statement are ignored [-Wattributes]
    6 |     [[maybe_unused]] f();
      |     ^~~~~~~~~~~~~~~~

Le (void)travail de fonte ne semble pas obligatoire mais est "encouragé" dans la norme: Comment puis-je supprimer intentionnellement une valeur de retour [[nodiscard]]?

Comme le montre également le message d'avertissement, une «solution» à l'avertissement consiste à ajouter -Wno-unused-result:

g++ -std=c++17 -ggdb3 -O0 -Wall -Wextra -pedantic -Wno-unused-result -o main.out main.cpp

bien que je ne recommanderais bien sûr pas d'ignorer les avertissements globalement comme celui-ci.

C ++ 20 vous permet également d'ajouter une raison de l' nodiscardcomme [[nodiscard("reason")]]mentionné à: https://en.cppreference.com/w/cpp/language/attributes/nodiscard

warn_unused_resultAttribut GCC

Avant la standardisation de [[nodiscard]], et pour C, avant de décider finalement de standardiser les attributs, GCC a implémenté exactement la même fonctionnalité avec warn_unused_result:

int f() __attribute__ ((warn_unused_result));

int f() {
    return 1;
}

int main() {
    f();
}

qui donne:

main.cpp: In function int main()’:
main.cpp:8:6: warning: ignoring return value of int f()’, declared with attribute warn_unused_result [-Wunused-result]
    8 |     f();
      |     ~^~

Il convient de noter alors que puisque ANSI C n'a pas de norme pour cela, ANSI C ne spécifie pas quelles fonctions de bibliothèque standard C ont l'attribut ou non et par conséquent, les implémentations ont pris leurs propres décisions sur ce qui devrait ou non être marqué warn_unuesd_result, qui C'est pourquoi, en général, vous devriez utiliser le (void)cast pour ignorer les retours de tout appel aux fonctions de bibliothèque standard pour éviter complètement les avertissements dans toute implémentation.

Testé dans GCC 9.2.1, Ubuntu 19.10.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
la source