Algo que me vejo fazendo frequentemente ultimamente é declarar typedefs relevantes para uma classe específica dentro dessa classe, ou seja,
class Lorem
{
typedef boost::shared_ptr<Lorem> ptr;
typedef std::vector<Lorem::ptr> vector;
//
// ...
//
};
Esses tipos são usados em outras partes do código:
Lorem::vector lorems;
Lorem::ptr lorem( new Lorem() );
lorems.push_back( lorem );
Razões pelas quais eu gosto:
- Reduz o ruído introduzido pelos modelos de classe,
std::vector<Lorem>
torna-seLorem::vector
, etc. - Serve como uma declaração de intenções - no exemplo acima, a classe Lorem deve ser contada como referência
boost::shared_ptr
e armazenada em um vetor. - Ele permite que a implementação mude - isto é, se o Lorem precisou ser alterado para ser contado intrusivamente como referência (via
boost::intrusive_ptr
) posteriormente, isso teria um impacto mínimo no código. - Eu acho que parece "mais bonito" e é sem dúvida mais fácil de ler.
Razões pelas quais eu não gosto:
- Às vezes, há problemas com dependências - se você deseja incorporar, digamos, um
Lorem::vector
dentro de outra classe, mas só precisa (ou deseja) encaminhar declarar Lorem (em vez de introduzir uma dependência em seu arquivo de cabeçalho), então você acaba usando o tipos explícitos (por exemplo, emboost::shared_ptr<Lorem>
vez deLorem::ptr
), o que é um pouco inconsistente. - Pode não ser muito comum e, portanto, mais difícil de entender?
Eu tento ser objetivo com o meu estilo de codificação, então seria bom ter outras opiniões sobre ele para poder dissecar um pouco o meu pensamento.
c++
coding-style
typedef
Will Baker
fonte
fonte
É exatamente isso que não faz.
Se eu vir 'Foo :: Ptr' no código, não faço a menor idéia se é um shared_ptr ou um Foo * (a STL tem :: pointer typedefs que são T *, lembre-se) ou o que for. Esp. se for um ponteiro compartilhado, não forneço um typedef, mas mantenho o shared_ptr explicitamente no código.
Na verdade, eu quase nunca uso typedefs fora da metaprogramação de modelos.
O design do STL com conceitos definidos em termos de funções-membro e typedefs aninhados é um beco sem saída histórico, as bibliotecas de modelos modernas usam funções gratuitas e classes de características (cf. Boost.Graph), porque elas não excluem tipos internos de modelar o conceito e porque facilita a adaptação de tipos que não foram projetados com os conceitos das bibliotecas de modelos fornecidas.
Não use o STL como motivo para cometer os mesmos erros.
fonte
std::allocator_traits<Alloc>
classe ... você não precisa se especializar para cada alocador que escreve, porque simplesmente empresta os tipos diretamenteAlloc
.Alloc
autor, não é exatamente mais difícil se especializarstd::allocator_traits<>
para seu novo tipo do que adicionar os typedefs necessários. Também editei a resposta, porque minha resposta completa não cabia em um comentário.allocator_traits
tentar criar um alocador personalizado, não preciso me preocupar com os quinze membros da classe de características ... tudo o que preciso fazer é dizertypedef Blah value_type;
e fornecer as funções de membro apropriadas, e o padrãoallocator_traits
descobrirá o descansar. Além disso, veja o seu exemplo de Boost.Graph. Sim, ele faz uso pesado da classe de características ... mas a implementação padrão degraph_traits<G>
simplesmente consultaG
seus próprios typedefs internos.iterator_traits
classe para que seus algoritmos genéricos possam consultar facilmente as informações apropriadas. O que, novamente, assume como padrão consultar o iterador para obter suas próprias informações. O longo e curto disso é que os traços e os typedefs internos dificilmente são mutuamente exclusivos ... eles se apoiam.iterator_traits
tornou - se necessário porqueT*
deveria ser um modelo deRandomAccessIterator
, mas você não pode colocar os typedefs necessáriosT*
. Uma vez que tivéssemositerator_traits
, os typedefs aninhados se tornaram redundantes, e eu gostaria que eles fossem removidos de vez em quando. Pelo mesmo motivo (impossibilidade de adicionar typedefs internos),T[N]
não modela oSequence
conceito STL e você precisa de kludges comostd::array<T,N>
. O Boost.Range mostra como um conceito moderno de Sequência pode ser definidoT[N]
para modelar, porque não requer typedefs aninhados nem funções-membro.Os typedefs são aqueles sobre os quais o design e as características baseados em políticas são construídos no C ++, portanto, o poder da Programação Genérica no C ++ deriva dos próprios typedefs.
fonte
Typdefs são definitivamente são de bom estilo. E todas as suas "razões de que gosto" são boas e corretas.
Sobre os problemas que você tem com isso. Bem, a declaração direta não é um santo graal. Você pode simplesmente criar seu código para evitar dependências de vários níveis.
Você pode mover typedef para fora da classe, mas Class :: ptr é muito mais bonito que ClassPtr que eu não faço isso. É como nos namespaces quanto a mim - as coisas permanecem conectadas dentro do escopo.
As vezes eu fiz
E pode ser o padrão para todas as classes de domínio e com alguma especialização para determinadas.
fonte
O STL faz esse tipo de coisa o tempo todo - os typedefs fazem parte da interface para muitas classes no STL.
são todos os typedefs que fazem parte da interface para várias classes de modelos STL.
fonte
Outro voto para esta ser uma boa ideia. Comecei a fazer isso ao escrever uma simulação que precisava ser eficiente, no tempo e no espaço. Todos os tipos de valores tinham um tytref Ptr iniciado como um ponteiro compartilhado de aumento. Fiz alguns perfis e mudei alguns deles para um ponteiro intrusivo de aumento sem precisar alterar nenhum código em que esses objetos foram usados.
Observe que isso só funciona quando você sabe onde as classes serão usadas e que todos os usos têm os mesmos requisitos. Eu não usaria isso no código da biblioteca, por exemplo, porque você não pode saber ao escrever a biblioteca o contexto em que ela será usada.
fonte
Atualmente, estou trabalhando no código, que usa intensivamente esse tipo de typedefs. Até agora tudo bem.
Mas notei que muitas vezes existem typedefs iterativos, as definições são divididas entre várias classes e você nunca sabe realmente com que tipo está lidando. Minha tarefa é resumir o tamanho de algumas estruturas de dados complexas ocultas por trás desses typedefs - portanto, não posso confiar nas interfaces existentes. Em combinação com três a seis níveis de namespaces aninhados, torna-se confuso.
Portanto, antes de usá-los, há alguns pontos a serem considerados
fonte
Eu recomendo mover esses typedefs para fora da classe. Dessa forma, você remove a dependência direta de ponteiros compartilhados e classes de vetores e pode incluí-los somente quando necessário. A menos que você esteja usando esses tipos em sua implementação de classe, considero que eles não devem ser typedefs internos.
As razões pelas quais você gosta ainda são correspondidas, pois são resolvidas pelo tipo de aliasing através de typedef, não declarando-as dentro da sua classe.
fonte
Quando o typedef é usado apenas dentro da própria classe (isto é, é declarado como privado), acho uma boa ideia. No entanto, exatamente pelas razões que você deu, eu não o usaria se os typedef precisassem ser conhecidos fora da classe. Nesse caso, recomendo movê-los para fora da classe.
fonte