Algo que me he encontrado haciendo a menudo últimamente es declarar typedefs relevantes para una clase particular dentro de esa clase, es decir
class Lorem
{
typedef boost::shared_ptr<Lorem> ptr;
typedef std::vector<Lorem::ptr> vector;
//
// ...
//
};
Estos tipos se usan luego en otra parte del código:
Lorem::vector lorems;
Lorem::ptr lorem( new Lorem() );
lorems.push_back( lorem );
Razones por las que me gusta:
- Reduce el ruido introducido por las plantillas de clase, se
std::vector<Lorem>
convierteLorem::vector
, etc. - Sirve como una declaración de intenciones: en el ejemplo anterior, la clase Lorem está destinada a ser contada por referencia
boost::shared_ptr
y almacenada en un vector. - Permite que la implementación cambie, es decir, si Lorem necesita ser cambiado para contar de manera intrusiva (vía
boost::intrusive_ptr
) en una etapa posterior, esto tendría un impacto mínimo en el código. - Creo que se ve más bonito y podría decirse que es más fácil de leer.
Razones por las que no me gusta:
- A veces hay problemas con las dependencias: si desea incrustar, por ejemplo, un
Lorem::vector
dentro de otra clase pero solo necesita (o desea) declarar Lorem (en lugar de introducir una dependencia en su archivo de encabezado), entonces terminará teniendo que usar el tipos explícitos (por ejemplo, enboost::shared_ptr<Lorem>
lugar deLorem::ptr
), lo cual es un poco inconsistente. - Puede que no sea muy común y, por lo tanto, más difícil de entender.
Trato de ser objetivo con mi estilo de codificación, por lo que sería bueno obtener algunas otras opiniones al respecto para poder analizar un poco mi pensamiento.
c++
coding-style
typedef
Will Baker
fuente
fuente
Esto es exactamente lo que no hace.
Si veo 'Foo :: Ptr' en el código, no tengo ni idea de si es un shared_ptr o un Foo * (STL tiene :: punteros typedefs que son T *, recuerde) o lo que sea. Esp. si es un puntero compartido, no proporciono un typedef en absoluto, pero mantengo el uso shared_ptr explícitamente en el código.
En realidad, casi nunca uso typedefs fuera de Template Metaprogramming.
El diseño STL con conceptos definidos en términos de funciones miembro y typedefs anidados es un callejón sin salida histórico, las bibliotecas de plantillas modernas usan funciones libres y clases de rasgos (véase Boost.Graph), porque estos no excluyen los tipos incorporados de modelando el concepto y porque facilita la adaptación de tipos que no fueron diseñados teniendo en cuenta los conceptos de las bibliotecas de plantillas dadas.
No use el STL como una razón para cometer los mismos errores.
fuente
std::allocator_traits<Alloc>
clase ... no tiene que especializarse para cada asignador que escriba, porque simplemente toma prestados los tipos directamenteAlloc
.Alloc
autor, no es exactamente más difícil especializarsestd::allocator_traits<>
para su nuevo tipo que agregar los tipos de letra necesarios. También he editado la respuesta, porque mi respuesta completa no cabía en un comentario.allocator_traits
intentar crear un asignador personalizado, no tengo que molestarme con los quince miembros de la clase de rasgos ... todo lo que tengo que hacer es decirtypedef Blah value_type;
y proporcionar las funciones de miembro apropiadas, y lo predeterminadoallocator_traits
determinará el descanso. Además, mira tu ejemplo de Boost.Graph. Sí, hace un uso intensivo de la clase de rasgos ... pero la implementación predeterminada degraph_traits<G>
simplemente consultasG
para sus propios typedefs internos.iterator_traits
clase para que sus algoritmos genéricos puedan consultar fácilmente la información adecuada. Lo cual, de nuevo, por defecto consulta al iterador por su propia información. En resumidas cuentas, los rasgos y las definiciones internas no son mutuamente excluyentes ... se apoyan mutuamente.iterator_traits
hizo necesario porqueT*
debería ser un modelo deRandomAccessIterator
, pero no puede poner los typedefs requeridosT*
. Una vez que lo hicimositerator_traits
, los typedefs anidados se volvieron redundantes, y desearía que hubieran sido eliminados allí y entonces. Por la misma razón (imposibilidad de agregar typedefs internos),T[N]
no modela elSequence
concepto STL , y necesita kludges comostd::array<T,N>
. Boost.Range muestra cómo se puede definir un concepto de secuencia moderno queT[N]
pueda modelar, ya que no requiere definiciones de tipo anidadas ni funciones miembro.Los typedefs son aquellos sobre los que se basan el diseño y los rasgos basados en políticas en C ++, por lo que el poder de la programación genérica en C ++ proviene de los typedefs mismos.
fuente
Typdefs definitivamente son buen estilo. Y todas sus "razones que me gustan" son buenas y correctas.
Sobre los problemas que tienes con eso. Bueno, la declaración adelantada no es un santo grial. Simplemente puede diseñar su código para evitar dependencias de varios niveles.
Puede mover typedef fuera de la clase, pero Class :: ptr es mucho más bonita que ClassPtr que no hago esto. Es como con los espacios de nombres en cuanto a mí: las cosas permanecen conectadas dentro del alcance.
A veces lo hice
Y puede ser predeterminado para todas las clases de dominio y con cierta especialización para ciertas.
fuente
El STL hace este tipo de cosas todo el tiempo: los typedefs son parte de la interfaz para muchas clases en el STL.
son todos los typedefs que forman parte de la interfaz para varias clases de plantillas STL.
fuente
Otra votación para que esta sea una buena idea. Comencé a hacer esto cuando escribía una simulación que tenía que ser eficiente, tanto en tiempo como en espacio. Todos los tipos de valor tenían una definición de tipo Ptr que comenzó como un puntero compartido de impulso. Luego hice algunos perfiles y cambié algunos de ellos a un puntero intrusivo de impulso sin tener que cambiar ninguno de los códigos donde se usaron estos objetos.
Tenga en cuenta que esto solo funciona cuando sabe dónde se utilizarán las clases y que todos los usos tienen los mismos requisitos. No usaría esto en el código de la biblioteca, por ejemplo, porque no puede saber al escribir la biblioteca el contexto en el que se usará.
fuente
Actualmente estoy trabajando en código, que usa intensivamente este tipo de typedefs. Hasta ahora eso está bien.
Pero me di cuenta de que a menudo hay tipos de letra iterativos, las definiciones se dividen entre varias clases, y nunca se sabe realmente con qué tipo se trata. Mi tarea es resumir el tamaño de algunas estructuras de datos complejas ocultas detrás de estos typedefs, por lo que no puedo confiar en las interfaces existentes. En combinación con tres a seis niveles de espacios de nombres anidados y luego se vuelve confuso.
Entonces, antes de usarlos, hay algunos puntos a considerar
fuente
Recomiendo mover esos typedefs fuera de la clase. De esta manera, elimina la dependencia directa en puntero compartido y clases de vectores y puede incluirlos solo cuando sea necesario. A menos que esté utilizando esos tipos en la implementación de su clase, considero que no deberían ser typedefs internos.
Las razones por las que te gusta aún coinciden, ya que se resuelven mediante el alias de tipo a través de typedef, no declarándolos dentro de tu clase.
fuente
Cuando typedef se usa solo dentro de la propia clase (es decir, se declara como privado), creo que es una buena idea. Sin embargo, por exactamente las razones que usted da, no lo usaría si los typedef necesitan ser conocidos fuera de la clase. En ese caso, recomiendo moverlos fuera de la clase.
fuente