2009-07-07 12 views
7

Estoy tratando de aprender las características actualmente aceptadas de C++ 11 y estoy teniendo problemas con auto y decltype. Como ejercicio de aprendizaje, extiendo la lista de clase estándar con algunas funciones genéricas.Usando auto y decltype en C++ 11

template<class _Ty, class _Ax = allocator<_Ty>> 
class FList : public std::list<_Ty, _Ax> 
{ 
public: 
    void iter(const function<void (_Ty)>& f) 
    { 
     for_each(begin(), end(), f); 
    } 

    auto map(const function<float (_Ty)>& f) -> FList<float>* 
    { 
     auto temp = new FList<float>(); 

     for (auto i = begin(); i != end(); i++) 
      temp->push_back(f(*i)); 

     return temp; 
    } 
}; 

auto *ints = new FList<int>(); 
ints->push_back(2); 
ints->iter([](int i) { cout << i; }); 

auto *floats = ints->map([](int i) { return (float)i; }); 
floats->iter([](float i) { cout << i; }); 

Para el mapa miembro Quiero el tipo de retorno para ser genérica dependiendo de lo que los rendimientos función pasada. Entonces, para el tipo de devolución, podría hacer algo como esto.

auto map(const function<float (_Ty)>& f) -> FList<decltype(f(_Ty))>* 

Esto también necesitaría eliminar el tipo de flotador en la plantilla de función.

auto map(const function<auto (_Ty)>& f) -> FList<decltype(f(_Ty))>* 

que podría utilizar una clase de plantilla, sino que hace que el uso de las instancias más detallado ya que tengo que especificar el tipo de retorno.

template<class T> FList<T>* map(const function<T (_Ty)>& f) 

a la suma de mi pregunta que estoy tratando de encontrar la manera de definir un mapa sin necesidad de utilizar una clase de plantilla y aún así tener que genérica en el tipo que devuelve.

Respuesta

17

Derivando de std::list u otros contenedores std:: se desaconseja.

Escriba sus operaciones como funciones gratuitas para que puedan funcionar en cualquier contenedor estándar a través de iteradores.

¿Quiere decir "definir el mapa sin utilizar una función de plantilla"?

Debería poder utilizar el tipo de miembro result_type de std::function para obtener el tipo que devuelve.

Además, no es necesario que especifique que la función se pasa como std::function. Podrías dejarlo abierto como cualquier tipo, y dejar que el compilador se una a todo. Solo necesita std::function para el polimorfismo en tiempo de ejecución.

¡Y usar nuevo para crear objetos de asignación de montón sin procesar y devolverlos por puntero es tan 1992! :)

Su función iter es esencialmente la misma que la range-based for loop.

Pero todo eso aparte ... ¿te refieres a algo como esto?

template <class TFunc> 
auto map(const TFunc &f) -> FList<decltype(f(_Ty()))>* 
{ 
    auto temp = new FList<decltype(f(_Ty()))>(); 

    for (auto i = begin(); i != end(); i++) 
     temp->push_back(f(*i)); 

    return temp; 
} 

Esto coincidirá con todo lo que se pueda llamar, y determinará el tipo de retorno de la función utilizando decltype.

Tenga en cuenta que se requiere _Ty para ser editable por defecto. Usted puede obtener en torno a que mediante la fabricación de una instancia:

template <class T> 
T make_instance(); 

se requiere ninguna aplicación porque no se genera un código que lo llama, por lo que el enlazador no tiene nada que quejarse

(gracias a dribeas por señalar esto!)

Así que el código se convierte ahora en:

FList<decltype(f(make_instance<_Ty>()))>* 

O, literalmente, una lista de lo que sea el tipo sería que usted recibiría en llamar a la función f con una referencia a una instancia de _Ty.

Y como prima libre para aceptar, buscar referencias rvalue - éstos significarán que se puede escribir:

std::list<C> make_list_somehow() 
{ 
    std::list<C> result; 
    // blah... 
    return result; 
} 

Y luego llamarlo así:

std::list<C> l(make_list_somehow()); 

Debido a std :: list tendrá un "move constructor" (como un constructor de copia pero elegido cuando el argumento es temporal, como aquí), puede robar el contenido del valor devuelto, es decir, hacer lo mismo que un óptimo swap. Entonces no hay copia de toda la lista. (Esta es la razón por la que C++ 0x hará que el código existente ingenuamente escrito se ejecute más rápido; muchos trucos de rendimiento populares pero desagradables se volverán obsoletos).

Y puede obtener el mismo tipo de cosas gratis para CUALQUIER clase existente, sin tener que escribir un constructor de movimiento correcto, usando unique_ptr.

std::unique_ptr<MyThing> myThing(make_my_thing_somehow()); 
+1

Estoy tratando de aprender a usar auto y decltype sin preguntar sobre las mejores prácticas. – gradbot

+3

Sí, pero hay muchas otras cosas de las que probablemente también te beneficiarías si tuvieras conocimiento. Estaba en un viaje en tren antes, así que me interrumpieron antes de llegar al tema del tipo declintivo. Actualizado ahora. –

+0

Awesome gracias! – gradbot

0

Creo que el punto que Jarrah estaba haciendo en su respuesta es que esos puntos sí explican exactamente cómo usar estas cosas. Voy a señalar los detalles de todas formas:

No se puede hacer esto, hay dos cosas malas:

auto map(const function<auto (_Ty)>& f) -> FList<decltype(f(_Ty))>* 

auto no se puede utilizar para argumentos de la función. Si desea que se deduzca el tipo de función, entonces debe usar plantillas. El segundo es que decltype toma una expresión, mientras que _Ty es un tipo. Aquí está cómo solucionarlo:

template <typename Ret> 
auto 
map(const function<Ret (_Ty)>& f) 
    -> FList<decltype(f(declval<_Ty>()))> 
{} 

De esa manera no hay magia para crear una instancia de un tipo.

Cuestiones relacionadas