Pregunta

Tengo el siguiente problema al usar la creación de instancias de plantilla [*].

archivo foo.h

class Foo
{
public:
    template <typename F>
    void func(F f)

private:
    int member_;
};

archivo foo.cc

template <typename F>
Foo::func(F f)
{
     f(member_);
}

archivo caller.cc

Foo::func(boost::bind(&Bar::bar_func, bar_instance, _1));

Si bien esto se compila bien, el enlazador se queja de un símbolo indefinido:

void Foo::func<boost::_bi::bind_t...>

¿Cómo puedo crear una instancia de la función Foo::func? Como toma una función como argumento, estoy un poco confundido. Traté de agregar una función de instanciación en foo.cc , como estoy acostumbrado con los tipos normales de sin función :

instantiate()
{
    template<> void Foo::func<boost::function<void(int)> >(boost::function<void(int)>);
}

Obviamente, esto no funciona. Le agradecería si alguien puede señalarme en la dirección correcta.

¡Gracias!

[*] Sí, leí el parashift FAQ lite.

¿Fue útil?

Solución

La respuesta a esto depende del compilador. Algunas versiones del compilador Sun C ++ manejarían esto automáticamente al construir un caché de implementaciones de funciones de plantilla que se compartirían en unidades de traducción separadas.

Si está utilizando Visual C ++ y cualquier otro compilador que no pueda hacer esto, también puede poner la definición de la función en el encabezado.

No se preocupe por las definiciones duplicadas si el encabezado está incluido por múltiples archivos .cc. El compilador marca los métodos generados por la plantilla con un atributo especial para que el vinculador sepa tirar los duplicados en lugar de quejarse. Esta es una razón por la cual C ++ tiene la & Quot; una regla de definición & Quot ;.

Editar: Los comentarios anteriores se aplican en el caso general donde su plantilla debe ser capaz de vincularse dados los parámetros de cualquier tipo. Si conoce un conjunto cerrado de tipos que usarán los clientes, puede asegurarse de que estén disponibles mediante la creación de instancias explícitas en el archivo de implementación de la plantilla, lo que hará que el compilador genere definiciones para que otros archivos se vinculen. Pero en el caso general donde su plantilla necesita trabajar con tipos posiblemente solo conocidos por el cliente, entonces tiene poco sentido separar la plantilla en un archivo de encabezado y un archivo de implementación; cualquier cliente debe incluir ambas partes de todos modos. Si desea aislar a los clientes de dependencias complejas, oculte esas dependencias detrás de funciones sin plantilla y luego instálelas desde el código de plantilla.

Otros consejos

Dividirlo en archivos como desee:
No es que recomiendo esto. Simplemente demostrando que es posible.

plop.h

#include <iostream>
class Foo
{
public:
    Foo(): member_(15){}


    // Note No definition of this in a header file.
    // It is defined in plop.cpp and a single instantiation forced
    // Without actually using it.
    template <typename F>
    void func(F f);

private:
    int member_;
};


struct Bar
{
     void bar_func(int val) { std::cout << val << "\n"; }
};

struct Tar
{
    void tar_func(int val) { std::cout << "This should not print because of specialisation of func\n";}
};

Plop.cpp

#include "plop.h"
#include <boost/bind.hpp>
#include <iostream>

template <typename F>
void Foo::func(F f)
{
     f(member_);
}

// Gnarly typedef
typedef boost::_bi::bind_t<void, boost::_mfi::mf1<void, Bar, int>, boost::_bi::list2<boost::_bi::value<Bar>, boost::arg<1> (*)()> > myFunc;

// Force the compiler to generate an instantiation of Foo::func()
template void Foo::func<myFunc>(myFunc f);

// Note this is not a specialization as that requires the <> after template.
// See main.cpp for an example of specialization.

main.cpp

#include "plop.h"
#include <boost/bind.hpp>
#include <iostream>

// Gnarly typedef
typedef boost::_bi::bind_t<void, boost::_mfi::mf1<void, Tar, int>, boost::_bi::list2<boost::_bi::value<Tar>, boost::arg<1> (*)()> > myTar;

// Specialization of Foo::func()
template<> void Foo::func<myTar>(myTar f)
{
    std::cout << "Special\n";
}
// Note. This is not instantiated unless it is used.
// But because it is used in main() we get a version.

int main(int argc,char* argv[])
{
    Foo f;
    Bar b;
    Tar t;

    f.func(boost::bind(&Bar::bar_func, b, _1)); // Uses instantiation from plop.cpp
    f.func(boost::bind(&Tar::tar_func, t, _1)); // Uses local specialization
}

¿Estás incluyendo foo.cc en caller.cc. La creación de instancias es algo que ocurre en el momento de la compilación: cuando el compilador ve la llamada en la persona que llama, crea versiones instanciadas de las plantillas, pero necesita tener disponible la definición completa.

Creo que a lo que ambos se refieren es que las definiciones de funciones de plantilla (no solo declaraciones) deben incluirse en el archivo donde se usan. Las funciones de plantilla en realidad no existen a menos que / hasta que se usen; si los coloca en un archivo cc separado, entonces el compilador no los conoce en los otros archivos cc, a menos que explique #include ese archivo cc en el archivo de encabezado o en el archivo que los llama, debido a la forma el analizador funciona.

(Es por eso que las definiciones de funciones de plantilla generalmente se mantienen en los archivos de encabezado, como lo describe Earwicker).

¿Más claro?

Creo que Earwicker es correcto. El problema con la instanciación explícita de la función de función de miembro de plantilla en este caso es que el tipo devuelto por boost :: bind depende de la implementación. Es no una función boost ::. Una función boost :: puede contener un boost: bind porque tiene un operador de asignación de plantilla que deduce el tipo del lado derecho (el resultado boost :: bind). En este uso particular de func en caller.cc, con esta implementación particular de boost, el tipo de boost :: bind es en realidad el tipo mencionado en el error del enlazador entre & Lt; y > (es decir, boost::_bi::bind_t...). Pero la creación de instancias explícitamente para ese tipo probablemente tendrá problemas de portabilidad.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top