Estou um pouco confuso sobre se e quando devo usar typedef em C ++. Eu sinto que é um ato de equilíbrio entre legibilidade e clareza.
Aqui está um exemplo de código sem nenhum typedefs:
int sum(std::vector<int>::const_iterator first,
std::vector<int>::const_iterator last)
{
static std::map<std::tuple<std::vector<int>::const_iterator,
std::vector<int>::const_iterator>,
int> lookup_table;
std::map<std::tuple<std::vector<int>::const_iterator,
std::vector<int>::const_iterator>, int>::iterator lookup_it =
lookup_table.find(lookup_key);
if (lookup_it != lookup_table.end())
return lookup_it->second;
...
}
OMI muito feio. Então, adicionarei alguns typedefs dentro da função para torná-la mais agradável:
int sum(std::vector<int>::const_iterator first,
std::vector<int>::const_iterator last)
{
typedef std::tuple<std::vector<int>::const_iterator,
std::vector<int>::const_iterator> Lookup_key;
typedef std::map<Lookup_key, int> Lookup_table;
static Lookup_table lookup_table;
Lookup_table::iterator lookup_it = lookup_table.find(lookup_key);
if (lookup_it != lookup_table.end())
return lookup_it->second;
...
}
O código ainda é um pouco desajeitado, mas me livrei da maioria dos materiais de pesadelo. Mas ainda existem os iteradores de vetor int, essa variante se livra deles:
typedef std::vector<int>::const_iterator Input_iterator;
int sum(Input_iterator first, Input_iterator last)
{
typedef std::tuple<Input_iterator, Input_iterator> Lookup_key;
typedef std::map<Lookup_key, int> Lookup_table;
static Lookup_table lookup_table;
Lookup_table::iterator lookup_it = lookup_table.find(lookup_key);
if (lookup_it != lookup_table.end())
return lookup_it->second;
...
}
Parece limpo, mas ainda é legível?
Quando devo usar um typedef? Assim que eu tiver um tipo de pesadelo? Assim que ocorre mais de uma vez? Onde devo colocá-los? Devo usá-los em assinaturas de funções ou mantê-los na implementação?
c++
coding-style
futlib
fonte
fonte
typedef Input_iterator std::vector<int>::const_iterator;
está ao contrário#define
não é bom o suficiente.Respostas:
Seu último exemplo é muito legível, mas depende de onde você define o typedef. Typedefs de escopo local (como no seu segundo exemplo) são IMVHO quase sempre uma vitória.
Ainda gosto do seu terceiro exemplo, mas você pode pensar na nomeação e fornecer aos iteradores nomes que informam a intenção do contêiner.
Outra opção seria transformar um modelo em sua função, para que também funcione com diferentes contêineres. Ao longo das linhas de
o que também está muito no espírito do STL.
fonte
Pense em a
typedef
como a declaração de variável equivalente a uma função: está lá para que você ...Pessoalmente, olho para cima se tiver que ler nomes de tipos longos
std::vector<int>::const_iterator
repetidamente.Seu terceiro exemplo não se repete desnecessariamente e é mais fácil de ler.
fonte
typedef
declarações servem essencialmente ao mesmo propósito que o encapsulamento. Por esse motivo, eles quase sempre se encaixam melhor em um arquivo de cabeçalho, seguindo as mesmas convenções de nomenclatura que suas classes, porque:typedef
, é provável que os chamadores também façam isso, especialmente no exemplo em que ele é usado nos argumentos.Como um aparte, seu código de memorização seria muito mais limpo se você o abstraísse ainda mais, como:
fonte