C ++ - неопределенная ссылка на недавно созданный класс! - PullRequest
0 голосов
/ 16 июня 2009

Я только что создал этот новый класс:

//------------------------------------------------------------------------------
#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
//------------------------------------------------------------------------------

Определение класса:

//------------------------------------------------------------------------------
#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_;

}

Теперь проблема в том, что когда я пытаюсь использовать его в своем коде в качестве статического члена другого класса:

// VehicleManager.h
#ifndef MULTITHREADEDVECTOR_H
#define MULTITHREADEDVECTOR_H

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

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

#endif

Тогда внутри: VehicleManager.cpp

#include "VehicleManager.h"

MultithreadedVector<Vehicle> VehicleManager::vehicles_;

void VehicleManager::Method() {

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

}

Но он не компилируется :(, я получаю сообщение об ошибке каждый раз:

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

Я действительно не понимаю, почему, тем более, что я определил статический член класса в глобальной области действия VehicleManager.cpp.

PS: я использую Code :: Blocks.

Спасибо!

Ответы [ 8 ]

5 голосов
/ 16 июня 2009

Вот что произойдет, если слепо переместить реализацию шаблона класса в файл cpp.

Чтобы это работало, вы должны точно знать, для каких типов T вы будете создавать экземпляр шаблона класса и указывать их в файле cpp.

В этом случае поместите это в файл cpp:

template class MultithreadedVector<Vehicle>;

Обратите внимание, что файл cpp должен знать о Vehicle тогда.

4 голосов
/ 16 июня 2009

Большинство компиляторов C ++ не позволяют разделять объявления шаблонов и определения шаблонов. Вам необходимо поместить полное определение ваших шаблонных классов в один файл .h, а не разбивать их на файлы .h и .cpp.

2 голосов
/ 17 июня 2009

Я думаю, что есть два случая, как можно использовать шаблоны

  1. Предоставление общего кода для произвольного набора соответствующих типов
  2. Предоставление общего кода для фиксированного набора типов

Во втором случае вам не нужно помещать шаблоны в заголовок: поскольку вы заранее точно знаете, с какими типами используется ваш шаблон. Вы можете очень хорошо определить функцию-член шаблона класса или шаблон функции в отдельном модуле перевода без ошибок компоновщика. Некоторые люди продолжают учить других, что это невозможно, и путают с этим дело.

Но я думаю, от этих двух случаев зависит, хотите ли вы разделить объявление и определение на разные единицы перевода или нет.

  • Если вы хотите использовать вариант 1, вы всегда хотите иметь определение в заголовке (независимо от того, включены ли они в специальные именованные файлы, такие как .ipp, .tcc или что-то еще). Мне известен только один интерфейс C ++, который поддерживает отделение определения от объявлений даже в этом случае, - это интерфейс EDG (группа разработки Edison), используемый компиляторами intel и comeau, которые реализуют export. Это ключевое слово считается ошибочным, и большинство компиляторов его не реализуют.

  • И если вы хотите использовать случай 2, не стоит помещать определения в заголовок, потому что для этого не было бы никакой причины: вы просто явно создадите экземпляры необходимых функций и классов для фиксированного набора типов. Во втором сценарии вы используете шаблон, чтобы избавить себя от нагрузки, чтобы поддерживать, возможно, избыточный код, и открываете опцию, чтобы ввести специальное поведение для некоторых типов в некоторых случаях.

Ваш сценарий явно относится к случаю 1, поэтому вам придется поместить свои определения в заголовок.

2 голосов
/ 16 июня 2009

Полный код реализации шаблона должен быть виден компилятору в той точке, в которой он видит использование шаблона, чтобы он мог создать экземпляр шаблона и скомпилировать его в объектный код, который впоследствии можно будет связать.

Определите код шаблона в том же заголовочном файле, в котором объявлен шаблон.

1 голос
/ 16 июня 2009

Шаблон в некотором роде является продвинутым, специализированным видом макросов. На самом деле вы не можете скомпилировать определение шаблона отдельно, как это выглядит, как будто вы пытаетесь это сделать.

Для большинства людей это означает, что они даже не удосуживаются отделить определения teplate от своих объявлений. У меня есть один сотрудник, который делает еще один шаг и отказывается отделять не шаблонные определения и декларации.

Если вы хотите добавить определение шаблона из декларации, у вас есть несколько вариантов.

Первый вариант - использовать ключевое слово C ++ "export". Проблема этого, казалось бы, простого решения в том, что никто не поддерживает . Это слишком сложно для разработчиков компиляторов.

Второй вариант - использовать третий тип файла , часто с тегом ".ipp", для объявлений. Хитрость в том, что это все еще файл, который должен быть «#included», но, будучи отдельным файлом, его нужно только включить в файлы «.cpp». Я обнаружил, что моему компоновщику не нравится, если я #include «.ipp» более чем в один «.cpp» файл в одной программе, поэтому вам нужно выбрать один из них.

1 голос
/ 16 июня 2009

Помимо других ответов:

Вы должны также охранять get_container(). Этот метод в основном копирует элементы container_ и, следовательно, должен быть защищен.

template<class T>
vector<T> MultithreadedVector<T>::get_container() 
{
    return container_;
}
1 голос
/ 16 июня 2009

Вы не можете записать реализацию шаблона в файл cpp в большинстве случаев. Переместите реализацию шаблона в файл заголовка, чтобы компилятор мог правильно его создать.

1 голос
/ 16 июня 2009

У вас, похоже, ошибка копирования и вставки в VehicleManager.h:

#ifndef MULTITHREADEDVECTOR_H
#define MULTITHREADEDVECTOR_H

Запрещает включение заголовочного файла, в котором определен класс Foo.

...