Pregunta

Acabo de crear esta nueva clase:

//------------------------------------------------------------------------------
#ifndef MULTITHREADEDVECTOR_H
#define MULTITHREADEDVECTOR_H
//------------------------------------------------------------------------------
#include <vector>
#include <GL/GLFW.h>
//------------------------------------------------------------------------------
template<class T>
class MultithreadedVector {

    public:

        MultithreadedVector();

        void push_back(T data);

        void erase(typename std::vector<T>::iterator it);

        std::vector<T> get_container();
    private:

        std::vector<T> container_;
        GLFWmutex th_mutex_;


};
//------------------------------------------------------------------------------
#endif // MULTITHREADEDVECTOR_H_INCLUDED
//------------------------------------------------------------------------------

La definición de la clase:

//------------------------------------------------------------------------------
#include "MultithreadedVector.h"
//------------------------------------------------------------------------------
using namespace std;
//------------------------------------------------------------------------------
template<class T>
MultithreadedVector<T>::MultithreadedVector() {

    th_mutex_ = glfwCreateMutex();
}

template<class T>
void MultithreadedVector<T>::push_back(T data) {

    glfwLockMutex(th_mutex_);
    container_.push_back(data);
    glfwUnlockMutex(th_mutex_);

}

template<class T>
void MultithreadedVector<T>::erase(typename vector<T>::iterator it) {

    glfwLockMutex(th_mutex_);
    container_.erase(it);
    glfwUnlockMutex(th_mutex_);
}

template<class T>
vector<T> MultithreadedVector<T>::get_container() {


    return container_;

}

Ahora el problema es que cuando intento usarlo en mi código como miembro estático de otra clase:

// VehicleManager.h
#ifndef MULTITHREADEDVECTOR_H
#define MULTITHREADEDVECTOR_H

#include "MultithreadedVector.h"
#include "Vehicle.h"
class Foo {

   public:
     // stuffs
   private:
     static MultithreadedVector<Vehicle> vehicles_; 
     ...
}

#endif

Luego adentro: VehicleManager.cpp

#include "VehicleManager.h"

MultithreadedVector<Vehicle> VehicleManager::vehicles_;

void VehicleManager::Method() {

  Vehicle vehicle;
  VehicleManager::vehicles_.push_back(vehicle);

}

Pero no compila :(, recibo este mensaje de error cada vez:

C:\***\VehicleManager.cpp|188|undefined reference to `MultithreadedVector<Vehicle>::push_back(Vehicle)'|

Realmente no entiendo por qué, especialmente que he definido el miembro de clase estática en el ámbito global de VehicleManager.cpp.

PS: Estoy usando Code :: Blocks.

¡Gracias!

¿Fue útil?

Solución

Eso es lo que sucederá cuando se mueva a ciegas una implementación de plantilla de clase a un archivo cpp.

Para hacer que funcione, debes saber exactamente para qué tipos de T crearás una instancia de la plantilla de clase y las especificarás en el archivo cpp.

En este caso, coloque esto en el archivo cpp:

template class MultithreadedVector<Vehicle>;

Tenga en cuenta que el archivo cpp debe saber sobre Vehicle entonces.

Otros consejos

La mayoría de los compiladores de C ++ no le permiten separar declaraciones de plantillas y definiciones de plantillas. Debe colocar la definición completa de sus clases de plantilla en un solo archivo .h, no dividirlas en archivos .h y .cpp.

El código completo de la implementación de la plantilla debe ser visible para el compilador en el momento en que vea el uso de la plantilla para que pueda crear una instancia de la plantilla y compilarla en un código objeto que se pueda vincular posteriormente.

Defina el código de la plantilla en el mismo archivo de encabezado en el que se declara la plantilla.

Creo que hay dos casos en los que se pueden usar plantillas

  1. Proporcionar código genérico para un conjunto arbitrario de tipos conformes
  2. Proporcionar código genérico para un conjunto fijo de tipos

Para el segundo, no es necesario que coloque plantillas en el encabezado: como sabe de antemano, con precisión, con qué tipo de plantilla se utiliza. Puede definir muy bien la función miembro de una plantilla de clase, o una plantilla de función en una unidad de traducción separada, sin errores de vinculador. Algunas personas siguen enseñando a otros que esto no es posible, y confunden el asunto con esto.

Pero creo que depende de estos dos casos si desea separar la declaración y la definición en diferentes unidades de traducción o no.

  • Si desea usar el caso 1, siempre desea tener la definición en el encabezado (ya sea que se incluya en archivos con nombres especiales, como .ipp , .tcc o lo que sea). Solo conozco una interfaz C ++ que permite separar la definición de las declaraciones incluso en este caso, que es la interfaz EDG (edison design group), utilizada por los compiladores intel y comeau, que implementan export . Esa palabra clave es vista como una característica incorrecta por algunos, y la mayoría de los compiladores no la implementan.

  • Y si desea usar el caso 2, no es bueno colocar las definiciones en el encabezado, porque no habría ninguna razón para hacerlo: simplemente instanciará explícitamente las funciones y clases necesarias para el conjunto fijo de los tipos. En este segundo escenario, usa la plantilla para salvarse de la carga para mantener el código posiblemente redundante, y tiene la opción abierta para introducir un comportamiento especial para algunos tipos en algunos casos.

Su escenario es claramente una situación de caso 1, por lo que tendrá que poner sus definiciones en el encabezado.

Parece que tienes un error de copiar y pegar en VehicleManager.h:

#ifndef MULTITHREADEDVECTOR_H
#define MULTITHREADEDVECTOR_H

Esto prohíbe la inclusión del archivo de encabezado, donde se define la clase Foo.

No puede escribir la implementación de la plantilla en el archivo cpp en la mayoría de los casos. Mueva la implementación de la plantilla al archivo de encabezado para que el compilador pueda crear una instancia correcta.

Aparte de las otras respuestas:

También tiene que proteger get_container () . Este método básicamente copia los elementos container_ y, por lo tanto, debe estar protegido.

template<class T>
vector<T> MultithreadedVector<T>::get_container() 
{
    return container_;
}

En cierto modo, una plantilla es un tipo de macro avanzada y especializada. En realidad, no puede compilar una definición de plantilla por separado, como parece que está intentando hacer.

Para la mayoría de las personas, esto significa que ni siquiera se molestan en separar las definiciones de teplate de sus declaraciones. Tengo un compañero de trabajo que lleva esto un paso más allá , y se niega a separar < Em> no-plantilla definiciones y declaraciones también.

Si quieres poner la definición de una plantilla a partir de la declaración, tienes un par de opciones.

La primera opción es usar C ++ " exportar " palabra clave. El problema con esta solución aparentemente simple es que nadie lo admite . Es demasiado difícil de implementar para los escritores de compiladores.

La segunda opción es usar un tercer tipo de archivo , a menudo etiquetado " .ipp " ;, para las declaraciones. El truco aquí es que todavía es un archivo que debe incluir " # incluido " ;, pero como archivo separado, solo tiene que incluirlo en " .cpp " archivos. He encontrado que no me gusta a mi vinculador si #incluyo un " .ipp " en más de un " .cpp " archivo en un solo programa, por lo que tiene que elegir uno para incluirlo.

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