¿Cuándo usar typedef?

13

Estoy un poco confundido acerca de si debo usar typedef en C ++ y cuándo. Creo que es un acto de equilibrio entre legibilidad y claridad.

Aquí hay un ejemplo de código sin ninguna definición de tipo:

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;            

    ...
}

IMO bastante feo. Así que agregaré algunos typedefs dentro de la función para que se vea mejor:

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;            

    ...
}

El código aún es un poco torpe, pero me deshago de la mayoría de los materiales de pesadilla. Pero todavía existen los itadores de vectores int, esta variante se deshace de ellos:

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;            

    ...
}

Esto se ve limpio, pero ¿sigue siendo legible?

¿Cuándo debo usar typedef? ¿Tan pronto como tenga un tipo de pesadilla? ¿Tan pronto como ocurre más de una vez? ¿Dónde debo ponerlos? ¿Debo usarlas en firmas de funciones o guardarlas en la implementación?

    
pregunta futlib 07.05.2012 - 07:05

3 respuestas

6

Su último ejemplo es muy legible, pero depende de dónde defina typedef. Los typedefs de alcance local (como en el segundo ejemplo) son IMVHO casi siempre una victoria.

Todavía me gusta más tu tercer ejemplo, pero es posible que desees pensar en el nombre y darle a los iteradores los nombres que indican la intención del contenedor.

Otra opción sería hacer una plantilla fuera de tu función, para que funcione con diferentes contenedores, también. En la línea de

template <typename Input_iterator> ... sum(Input_iterator first, Input_iterator last) 

que también está muy en el espíritu de la STL.

    
respondido por el Fabio Fracassi 07.05.2012 - 11:23
2

Piense en un typedef como la declaración de variable equivalente a una función: está ahí para que ...

  • ... no tiene que repetirse cuando reutilice el mismo tipo (como lo hacen sus dos primeros ejemplos).
  • ... puede ocultar los detalles sangrientos del tipo para que no estén siempre en exhibición.
  • ... asegúrese de que los cambios en el tipo se reflejen en cualquier lugar que se usen.

Personalmente, me quedo en blanco si tengo que leer nombres largos como std::vector<int>::const_iterator repetidamente.

Tu tercer ejemplo no se repite innecesariamente y es más fácil de leer.

    
respondido por el Blrfl 07.05.2012 - 16:15
1
Las declaraciones de

typedef sirven esencialmente para el mismo propósito que la encapsulación. Por esa razón, casi siempre encajan mejor en un archivo de encabezado, siguiendo las mismas convenciones de nomenclatura que sus clases, porque:

  • Si necesita el typedef , es probable que las personas que llaman también lo hagan, especialmente en su ejemplo donde se usa en los argumentos.
  • Si necesita cambiar el tipo por cualquier motivo, incluido el reemplazo con su propia clase, solo tendrá que hacerlo en un lugar.
  • Te hace menos propenso a cometer errores debido a la escritura repetida de tipos complejos.
  • Oculta detalles de implementación innecesarios.

Además, su código de memorización sería mucho más limpio si lo abstrae aún más, como:

if (lookup_table.exists(first, last))
    return lookup_table.get(first, last);
    
respondido por el Karl Bielefeldt 07.05.2012 - 18:22

Lea otras preguntas en las etiquetas