Com o GCC 4.8.0 lançado, temos um compilador que oferece suporte à dedução automática de tipo de retorno, parte do C ++ 14. Com -std=c++1y
, eu posso fazer isso:
auto foo() { //deduced to be int
return 5;
}
Minha pergunta é: quando devo usar esse recurso? Quando é necessário e quando torna o código mais limpo?
Cenário 1
O primeiro cenário em que consigo pensar é sempre que possível. Toda função que pode ser escrita dessa maneira deve ser. O problema é que nem sempre o código fica mais legível.
Cenário 2
O próximo cenário é evitar tipos de retorno mais complexos. Como um exemplo muito leve:
template<typename T, typename U>
auto add(T t, U u) { //almost deduced as decltype(t + u): decltype(auto) would
return t + u;
}
Eu não acredito que isso seja realmente um problema, embora eu ache que o tipo de retorno dependa explicitamente dos parâmetros possa ser mais claro em alguns casos.
Cenário 3
Em seguida, para evitar redundância:
auto foo() {
std::vector<std::map<std::pair<int, double>, int>> ret;
//fill ret in with stuff
return ret;
}
No C ++ 11, às vezes podemos apenas return {5, 6, 7};
substituir um vetor, mas isso nem sempre funciona e precisamos especificar o tipo no cabeçalho da função e no corpo da função. Isso é puramente redundante, e a dedução automática do tipo de retorno nos salva dessa redundância.
Cenário 4
Finalmente, ele pode ser usado no lugar de funções muito simples:
auto position() {
return pos_;
}
auto area() {
return length_ * width_;
}
Às vezes, porém, podemos olhar para a função, querendo saber o tipo exato, e se ela não for fornecida, precisamos ir para outro ponto do código, como onde pos_
é declarado.
Conclusão
Com os cenários apresentados, quais deles realmente provam ser uma situação em que esse recurso é útil para tornar o código mais limpo? E os cenários que deixei de mencionar aqui? Que precauções devo tomar antes de usar este recurso para que ele não me morda mais tarde? Existe algo novo que esse recurso traga para a mesa que não seja possível sem ele?
Observe que as múltiplas perguntas devem ajudar a encontrar perspectivas para responder a isso.
fonte
->decltype(t+u)
pela dedução automática mata o SFINAE.Respostas:
O C ++ 11 levanta questões semelhantes: quando usar a dedução de tipo de retorno em lambdas e quando usar
auto
variáveis.A resposta tradicional para a pergunta em C e C ++ 03 tem sido "através dos limites da declaração, tornamos os tipos explícitos, em expressões que geralmente são implícitas, mas podemos explicitá-las com moldes". C ++ 11 e C ++ 1y introduzem ferramentas de dedução de tipo para que você possa deixar de fora o tipo em novos locais.
Desculpe, mas você não vai resolver isso com antecedência, criando regras gerais. Você precisa examinar um código específico e decidir por si mesmo se ajuda ou não a legibilidade para especificar tipos em todo o lugar: é melhor para o seu código dizer "o tipo dessa coisa é X" ou é melhor para seu código para dizer: "o tipo disso é irrelevante para entender essa parte do código: o compilador precisa saber e provavelmente poderíamos resolver isso, mas não precisamos dizê-lo aqui"?
Como "legibilidade" não é objetivamente definida [*] e, além disso, varia de acordo com o leitor, você tem a responsabilidade como autor / editor de um trecho de código que não pode ser totalmente satisfeito por um guia de estilo. Mesmo na medida em que um guia de estilo especifica normas, pessoas diferentes preferem normas diferentes e tendem a achar que qualquer coisa estranha é "menos legível". Portanto, a legibilidade de uma regra de estilo proposta em particular geralmente pode ser julgada apenas no contexto das outras regras de estilo em vigor.
Todos os seus cenários (até o primeiro) encontrarão uso para o estilo de codificação de alguém. Pessoalmente, acho que o segundo é o caso de uso mais atraente, mas mesmo assim espero que isso dependa de suas ferramentas de documentação. Não é muito útil ver documentado que o tipo de retorno de um modelo de função é
auto
, enquanto vê-lo documentado comodecltype(t+u)
cria uma interface publicada na qual você pode (espero) confiar.[*] Ocasionalmente, alguém tenta fazer algumas medidas objetivas. Na pequena extensão em que alguém obtém resultados estatisticamente significativos e geralmente aplicáveis, eles são completamente ignorados pelos programadores que trabalham, em favor dos instintos do autor quanto ao que é "legível".
fonte
1.0 + 27U
, estamos afirmando o último, quando escrevemos(double)1.0 + (double)27U
, estamos afirmando o primeiro. A simplicidade da função, o grau de duplicação e a evasãodecltype
podem contribuir para isso, mas nada será decisivamente confiável.auto
em geral.auto
palavra-chave, ela exibirá o tipo de retorno real.int*
, mas o que é realmente significativo, se é que existe alguma coisa, é que o motivoint*
é porque é isso questd::vector<int>::iterator_type
está nas suas opções de compilação atuais!De um modo geral, o tipo de retorno da função é de grande ajuda para documentar uma função. O usuário saberá o que é esperado. No entanto, há um caso em que acho que seria bom descartar esse tipo de retorno para evitar redundância. Aqui está um exemplo:
Este exemplo é retirado do documento oficial do comitê N3493 . O objetivo da função
apply
é encaminhar os elementos de astd::tuple
para uma função e retornar o resultado. Oint_seq
emake_int_seq
são apenas parte da implementação e provavelmente confundirão qualquer usuário tentando entender o que ele faz.Como você pode ver, o tipo de retorno nada mais é do que uma
decltype
expressão retornada. Além disso,apply_
não sendo feito para ser visto pelos usuários, não tenho certeza da utilidade de documentar seu tipo de retorno quando ele é mais ou menos o mesmo queapply
o dele. Eu acho que, nesse caso em particular, largar o tipo de retorno torna a função mais legível. Observe que esse tipo de retorno foi realmente descartado e substituídodecltype(auto)
na proposta de adicionarapply
ao padrão N3915 (observe também que minha resposta original é anterior a este artigo):No entanto, na maioria das vezes, é melhor manter esse tipo de retorno. No caso específico que descrevi acima, o tipo de retorno é bastante ilegível e um usuário em potencial não ganha nada ao saber disso. Uma boa documentação com exemplos será muito mais útil.
Outra coisa que ainda não foi mencionada: embora
declype(t+u)
permita usar a expressão SFINAE ,decltype(auto)
não (embora exista uma proposta para alterar esse comportamento). Tomemos, por exemplo, umafoobar
função que chamará afoo
função membro de um tipo, se existir, ou chamará abar
função membro de um tipo, se existir, e suponha que uma classe sempre tenha exatidãofoo
oubar
mas nenhuma das duas ao mesmo tempo:A chamada
foobar
de uma instância deX
será exibidafoo
enquanto a chamadafoobar
de uma instância deY
será exibidabar
. Se você usar a dedução automática do tipo de retorno (com ou semdecltype(auto)
), não obterá a expressão SFINAE e invocaráfoobar
uma instância de qualquer uma delasX
ouY
acionará um erro em tempo de compilação.fonte
Isso nunca é necessário. Quanto a quando você deveria, você receberá muitas respostas diferentes sobre isso. Eu diria que não, até que seja realmente uma parte aceita do padrão e bem suportada pela maioria dos principais compiladores da mesma maneira.
Além disso, será um argumento religioso. Pessoalmente, eu diria que nunca colocar o tipo de retorno real torna o código mais claro, é muito mais fácil para manutenção (posso olhar para a assinatura de uma função e saber o que ela retorna versus realmente ter que ler o código) e remove a possibilidade de que você acha que deve retornar um tipo e o compilador pensa que outro está causando problemas (como aconteceu com todas as linguagens de script que eu já usei). Eu acho que o automóvel foi um erro gigante e causará muito mais dor do que ajuda em ordens de magnitude. Outros dirão que você deve usá-lo o tempo todo, pois ele se encaixa na filosofia de programação deles. De qualquer forma, isso está fora do escopo deste site.
fonte
#define
s para transformar C ++ em VB. Os recursos geralmente têm um bom consenso na mentalidade da linguagem sobre o que é o uso adequado e o que não é, de acordo com o que os programadores da linguagem estão acostumados. O mesmo recurso pode estar presente em vários idiomas, mas cada um tem suas próprias diretrizes sobre o uso dele.auto
é pura bênção. Remove muita redundância. É simplesmente doloroso repetir o tipo de retorno algumas vezes. Se você deseja retornar um lambda, pode ser até impossível sem armazenar o resultado em umstd::function
, o que pode resultar em alguma sobrecarga.auto
para que ele sempre corresponda ao tipo de retorno da função passada.auto
para o tipo de retorno à direita.]Não tem nada a ver com a simplicidade da função (como uma duplicata agora excluída desta pergunta deveria).
Ou o tipo de retorno é fixo (não use
auto
), ou dependente de uma forma complexa em um parâmetro do modelo (usoauto
na maioria dos casos, emparelhado comdecltype
quando há vários pontos de retorno).fonte
Considere um ambiente de produção real: muitas funções e testes de unidade são todos interdependentes no tipo de retorno
foo()
. Agora, suponha que o tipo de retorno precise ser alterado por qualquer motivo.Se o tipo de retorno estiver em
auto
qualquer lugar e os chamadoresfoo()
e as funções relacionadas usaremauto
ao obter o valor retornado, as alterações que precisam ser feitas são mínimas. Caso contrário, isso pode significar horas de trabalho extremamente tedioso e propenso a erros.Como exemplo do mundo real, fui convidado a mudar um módulo de usar ponteiros brutos em todos os lugares para ponteiros inteligentes. A correção dos testes de unidade foi mais dolorosa do que o código real.
Embora existam outras maneiras de lidar com isso, o uso de
auto
tipos de retorno parece ser um bom ajuste.fonte
decltype(foo())
?typedef
as declarações s e alias (ou seja,using
declaração de tipo) são melhores nesses casos, especialmente quando têm escopo de classe.Quero fornecer um exemplo em que o tipo de retorno auto seja perfeito:
Imagine que você deseja criar um pequeno alias para uma longa chamada de função subsequente. Com o auto, você não precisa cuidar do tipo de retorno original (talvez isso mude no futuro) e o usuário pode clicar na função original para obter o tipo de retorno real:
PS: Depende dessa pergunta.
fonte
Para o cenário 3, eu mudaria o tipo de retorno da assinatura da função com a variável local a ser retornada. Isso tornaria mais claro para os programadores clientes que a função retorna. Como isso:
Cenário 3 Para evitar redundância:
Sim, não possui palavra-chave automática, mas o principal é o mesmo para evitar redundância e facilitar aos programadores que não têm acesso à fonte.
fonte
vector<map<pair<int,double>,int>
e usá-lo.