Por que lançar valores de retorno não utilizados para nulos?


112
int fn();

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

Existe alguma razão para lançar um valor de retorno não utilizado para o vazio, ou estou certo em pensar que é uma completa perda de tempo?

Acompanhamento:

Bem, isso parece muito abrangente. Suponho que seja melhor do que comentar um valor de retorno não utilizado, uma vez que o código de autodocumentação é melhor do que comentários. Pessoalmente, desativarei esses avisos, pois é um ruído desnecessário.

Vou engolir minhas palavras se um bug escapar por causa disso ...

Respostas:


79

A resposta de David cobre praticamente a motivação para isso, para mostrar explicitamente a outros "desenvolvedores" que você sabe que essa função retorna, mas está explicitamente ignorando-a.

Esta é uma forma de garantir que, quando necessário, os códigos de erro sejam sempre tratados.

Acho que para C ++ este é provavelmente o único lugar em que prefiro usar conversões no estilo C também, já que usar a notação de conversão estática completa parece um exagero aqui. Por fim, se você estiver revisando um padrão de codificação ou escrevendo um, também é uma boa ideia declarar explicitamente que as chamadas para operadores sobrecarregados (sem usar notação de chamada de função) também devem ser isentas disso:

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.

este é o único lugar onde eu também prefiro elenco de estilo c. embora no meu padrão de codificação, eu adicionaria (vazio) a "a + a;" também (corretamente pareado, é claro: p)
Johannes Schaub - litb

8
Um pouco fora do assunto, mas por que você faria "a + a;" assim sem usar o valor de retorno? Isso realmente parece um abuso de efeitos colaterais para mim e ofusca a intenção do código.
Rob K

5
Bem, aquele que você usará todos os dias será 'a = a'. Isso retorna o atribuído ao objeto (para todas as classes de bom comportamento que é! :)). Outro exemplo é: os << "Hello World" << std :: endl. Cada um deles retorna o objeto "os".
Richard Corden

46

No trabalho, usamos isso para reconhecer que a função tem um valor de retorno, mas o desenvolvedor afirmou que é seguro ignorá-lo. Já que você marcou a questão como C ++, você deve usar static_cast :

static_cast<void>(fn());

Até onde o compilador vai lançar o valor de retorno para void tem pouco significado.


Suprime o aviso sobre valores de retorno não utilizados?
Paul Tomblin

Não, não tem. @Mykola: Com GCC4 você pode anexar um atributo que diz que o valor de retorno não deve ser ignorado, o que irá disparar um aviso.
David Holm

2
Eu uso g ++ e dá aviso mesmo com tal elenco.
klew

2
qual opção de aviso você usa? -Wunused-value não dispara o aviso em meu ambiente
Mykola Golubyev

No VC ++, ele suprime o aviso
jalf

39

A verdadeira razão para fazer isso remonta a uma ferramenta usada no código C, chamada lint .

Ele analisa o código procurando possíveis problemas e emitindo avisos e sugestões. Se uma função retornasse um valor que não foi verificado, lintavisaria caso isso fosse acidental. Para silenciar linteste aviso, você lançou a chamada para (void).


2
Pode ter começado desta forma, mas a maioria das ferramentas agora possui outros mecanismos para suprimir avisos como este. Além disso - independentemente do motivo pelo qual isso começou, no domínio do código crítico de segurança em particular, essa se tornou a maneira usual de "documentar" as intenções do desenvolvedor.
Richard Corden

5
O GCC dá um aviso para isso com -Wall.
greyfade

23

Casting to voidé usado para suprimir avisos do compilador para variáveis ​​não usadas e valores de retorno ou expressões não salvos.

The Standard (2003) diz em §5.2.9 / 4 diz,

Qualquer expressão pode ser explicitamente convertida para o tipo “cv void”. O valor da expressão é descartado .

Então você pode escrever:

//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());

Todos os formulários são válidos. Eu geralmente o torno mais curto como:

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

Também está bem.


1
Exceto que nem sempre desativa os avisos do compilador. gcc não parece responder ao lançamento para void
Matt

@Matt: Qual versão do GCC você está usando? Você pode postar seu código em ideone.com ou stacked-crooked.com (o último é melhor).
Nawaz de

1
O texto padrão "descartado" implica que o aviso não deve ser levantado por linters?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

2
@CiroSantilli 巴拿馬 文件 六四 事件 法轮功: .... essa é uma pergunta interessante. Atualmente, não sei a resposta para isso. Por favor, compartilhe comigo se você souber disso de qualquer maneira.
Nawaz


4

Cast para vazio não tem custo. São apenas informações para o compilador como tratá-lo.


1
É "gratuito" se o tempo para escrevê-lo e o tempo subsequente para ler, entender (e depois ignorar) o código for gratuito. IMO, escrever (void)custa-me tempo e energia e faz-me pensar se o autor sabia como funcionam as expressões.
wallyk

4

Pois a funcionalidade de seu programa de lançamento para anular não tem sentido. Eu também diria que você não deve usá-lo para sinalizar algo para a pessoa que está lendo o código, como sugerido na resposta de David. Se você deseja comunicar algo sobre suas intenções, é melhor usar um comentário. Adicionar um elenco como este só parecerá estranho e levantará questões sobre o possível motivo. Apenas minha opinião...


2
Este é um padrão conhecido para dizer explicitamente aos outros que você não se importa com o valor de retorno, que você não apenas se esqueceu de manipulá-lo.
Spidey

For the functionality of you program casting to void is meaninglessPara autodocumentação e o número de avisos durante a compilação, realmente não é. you should not use it to signal something to the person that is reading the code [...] it is better to use a comment.O melhor comentário é nenhum comentário , quando o código se explica. E, novamente, mantém um compilador de aviso válido feliz no processo.
sublinhado_d

'Adicionar um elenco como este só vai parecer estranho e levantar questões sobre o possível motivo'. Eu descobri um desses e é por isso que estou lendo isso. Parecia um abuso de pilha misterioso para meu cérebro destreinado.
mynameisnafe de

1

Além disso, ao verificar se seu código está em conformidade com os padrões MISTA (ou outros), ferramentas automáticas como LDRA não permitirão que você chame uma função que tenha um tipo de retorno sem que ela retorne um valor, a menos que você converta explicitamente o valor retornado em (void)


0

C ++ 17 [[nodiscard]]

C ++ 17 padronizou o "valor de retorno do negócio ignorado" com um atributo.

Portanto, espero que as implementações compatíveis sempre avisem apenas quando nodiscardforem fornecidas e nunca avisem o contrário.

Exemplo:

main.cpp

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

int main() {
    f();
}

compilar:

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

resultado:

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() {
      | 

Todos os itens a seguir evitam o aviso:

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

Não consegui usar maybe_unuseddiretamente na f()chamada:

[[maybe_unused]] f();

dá:

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

O (void)trabalho do elenco não parece ser obrigatório, mas é "encorajado" no padrão: Como posso descartar intencionalmente um valor de retorno [[nodiscard]]?

Também como visto na mensagem de aviso, uma "solução" para o aviso é adicionar -Wno-unused-result:

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

embora eu não recomende, é claro, ignorar avisos globalmente como este.

C ++ 20 também permite que você adicione uma razão para o nodiscardque no [[nodiscard("reason")]]como mencionado em: https://en.cppreference.com/w/cpp/language/attributes/nodiscard

warn_unused_resultAtributo GCC

Antes da padronização de [[nodiscard]]e para C antes de finalmente decidirem padronizar atributos, o GCC implementou exatamente a mesma funcionalidade com warn_unused_result:

int f() __attribute__ ((warn_unused_result));

int f() {
    return 1;
}

int main() {
    f();
}

que dá:

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();
      |     ~^~

Deve-se notar então que, uma vez que ANSI C não tem um padrão para isso, ANSI C não especifica quais funções de biblioteca padrão C têm o atributo ou não e, portanto, as implementações tomaram suas próprias decisões sobre o que deve ou não ser marcado com warn_unuesd_result, qual é por isso que, em geral, você teria que usar o (void)elenco para ignorar retornos de quaisquer chamadas para funções de biblioteca padrão para evitar totalmente os avisos em qualquer implementação.

Testado em GCC 9.2.1, Ubuntu 19.10.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.