Em um código novo que não precisa ser compatível com padrões mais antigos, use esse atributo onde quer que seja sensato. Mas para C ++, [[nodiscard]]
faz um padrão incorreto. Você sugere:
It would seem much more natural to make compilers warn about discarded return values by default except for the few other cases where you mark your intention.
Isso faria com que o código correto existente emitisse muitos avisos. Embora essa mudança tecnicamente possa ser considerada compatível com versões anteriores, já que qualquer código existente ainda é compilado com sucesso, isso seria uma grande mudança de semântica na prática.
Decisões de design para uma linguagem madura existente com uma base de código muito grande são necessariamente diferentes das decisões de design para um idioma completamente novo. Se esta fosse uma nova língua, então advertir por padrão seria sensato. Por exemplo, a linguagem Nim requer que valores desnecessários sejam descartados explicitamente - isso seria semelhante a envolver todas as declarações de expressão em C ++ com um cast (void)(...)
.
Um atributo [[nodiscard]]
é mais útil em dois casos:
-
se uma função não tiver efeitos além de retornar um determinado resultado, ou seja, é pura. Se o resultado não for usado, a chamada é certamente inútil. Por outro lado, descartar o resultado não seria incorreto.
-
se o valor de retorno precisar ser verificado, por exemplo para uma interface semelhante a C que retorna códigos de erro em vez de lançar. Este é o caso de uso principal. Para o idiomático C ++, isso será bastante raro.
Estes dois casos deixam um enorme espaço de funções impuras que retornam um valor, onde tal aviso seria enganoso. Por exemplo:
-
Considere um tipo de dados da fila com um método
.pop()
que remove um elemento e retorna uma cópia do elemento removido. Tal método é frequentemente conveniente. No entanto, existem alguns casos em que apenas queremos remover o elemento, sem obter uma cópia. Isso é perfeitamente legítimo e um aviso não seria útil. Um design diferente (comostd::vector
) divide essas responsabilidades, mas isso tem outras compensações.Note que, em alguns casos, uma cópia deve ser feita de qualquer maneira, então, graças ao retorno da RVO, a cópia será gratuita.
-
Considere interfaces fluentes, onde cada operação retorna o objeto para que outras operações possam ser realizadas. Em C ++, o exemplo mais comum é o operador de inserção de fluxo
<<
. Seria extremamente complicado adicionar um atributo[[nodiscard]]
a cada<<
de sobrecarga.
Estes exemplos demonstram que fazer código idiomático em C ++ compilar sem avisos sob uma linguagem “C ++ 17 com nodiscard por padrão” seria bastante entediante.
Observe que o seu novo e brilhante código C ++ 17 (onde você pode usar esses atributos) ainda pode ser compilado junto com bibliotecas que visam padrões C ++ mais antigos. Essa compatibilidade com versões anteriores é crucial para o ecossistema C / C ++. Assim, tornar o padrão nodiscard resultaria em muitos avisos para casos de uso idiomáticos típicos - avisos que você não pode corrigir sem alterações de longo alcance no código-fonte da biblioteca.
Indiscutivelmente, o problema aqui não é a mudança de semântica, mas que os recursos de cada padrão C ++ se aplicam a um escopo de unidade de compilação e não a um escopo por arquivo. Se / quando algum padrão futuro de C ++ se afastar dos arquivos de cabeçalho, tal mudança seria mais realista.