2016-06-01 3 views
0

В последнее время я играл с std::map и придумал грандиозный дизайн D для создания карты приоритетов - карты, которая содержит различные режимы, которые сгруппированы исходя из их приоритетов.Функция члена шаблона в классе без шаблона - как определить и позвонить

Я имею следующую структуру класса:

Mode 
| 
|----ModeSleep 
|----ModeFactorial 

где Mode является:

class Mode 
{ 
    std::string name; ///< Mode's name 
    int priority;  ///< Mode's priority used for storing the Mode in a specific priority group in the priority map. Default: 0 
    public: 
    Mode(); 
    /// 
    /// \brief Mode 
    /// \param name Mode's name 
    /// \param priority Mode's priority used for storing the Mode in a specific priority group in the priority map. Default: 0 
    /// 
    Mode(const std::string &name, const int priority=0); 
    virtual ~Mode(); 

    std::string getName() const; 
    void setName(const std::string &value); 

    int getPriority() const; 
    void setPriority(int value); 

    /// 
    /// \brief run is the part of a Mode which is executed by the ModeExecutor 
    /// 
    virtual void run() = 0; 
}; 

С другой стороны, у меня есть еще один класс, который использует Mode под названием PriorityMap со следующим определением класса:

class PriorityMap 
{ 
    typedef std::pair<int, Mode *> ModeEntry; 
    typedef std::map<int, Mode *> PriorityGroup; 
    typedef PriorityGroup* PriorityGroup_Ptr; 
    typedef std::map<int, PriorityGroup_Ptr> Priority; 
    typedef Priority* Priority_Ptr; 

    Priority_Ptr priorities; 

    bool _insert(Mode *mode); 
    Mode *_find(const std::string &name); 
    public: 
    PriorityMap(); 
    ~PriorityMap(); 

    void print(); 
    void insert(Mode *mode); 
    template<class T> T *find(const std::string &name); 
}; 

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

int main() 
{ 
    PriorityMap *priorities = new PriorityMap(); 
    ModeSleep *m1 = new ModeSleep("Sleep10", 0, 10); 
    priorities->insert(m1); 

    ModeSleep *m2 = new ModeSleep("Sleep5", 0, 5); 
    priorities->insert(m2); 

    ModeFactorial *m3 = new ModeFactorial("Factorial20", 1, 20); 
    priorities->insert(m3); 

    priorities->print(); 

    // Example for a correct match (both name and type) - ERROR!!! 
    ModeSleep *foundM2 = priorities->template find<ModeSleep>("Sleep5"); 
    if(foundM2) 
    std::cout << "Found mode \"" << foundM2->getName() << "\" has time interval set to " << foundM2->getMilliseconds() << "ms" << std::endl; 

    // Example for correct name match but incorrect type - ERROR!!! 
    ModeSleep *foundM1 = priorities->template find<ModeSleep>("Factorial20"); 
    if(foundM1) 
    std::cout << "Found mode \"" << foundM1->getName() << "\" has time interval set to " << foundM1->getMilliseconds() << "ms" << std::endl; 

    delete priorities; 

    return 0; 
} 

В первый у меня не было каких-либо шаблонный материал для моего find() (когда я переехал оригинальный find() как частное _find() под названием внутри версии templatefind()). Мой первоначальный дизайн (теперь _find()) был:

Mode *PriorityMap::_find(const std::string &name) 
{ 
    for(const auto& priorityGroup : *priorities) 
    for(auto& modeEntry : *(priorityGroup.second)) 
     if(!name.compare((modeEntry.second->getName()))) 
     return modeEntry.second; 

    return nullptr; 
} 

После запуска find() пар раза я столкнулся с проблемой, что мне пришлось вручную опущенные возвращаемый указатель к соответствующему выводу Mode (в моем случае просто ModeSleep и ModeFactorial). Поэтому я решил, что добавление шаблонной функциональности для этой функции, а также добавляя некоторую обратную связь при вызове было бы полезно:

template<class T> 
T *PriorityMap::find(const std::string &name) 
{ 
    Mode *foundMode = _find(name); 
    if(foundMode) { 
    T *foundModeCast = dynamic_cast<T *>(foundMode); 
    if(foundModeCast) { 
     std::cout << "Found mode \"" << foundModeCast->getName() << "\"" << std::endl; 
     return foundModeCast; 
    } 
    else { 
     std::cout << "Found mode \"" << foundMode->getName() << "\" however specified type is invalid! Returning NULL" << std::endl; 
     return nullptr; 
    } 
    } 
} 

Как вы можете видеть, по моему определению найден режим внутри мой приоритет карта основана от двух факторов:

  • name является матчем
  • данного типа спички

У меня есть проблема с вызовом моего find(), хотя и я моя сборка перерывы в первом использовании его со следующей ошибкой:

In function `main': 
undefined reference to `ModeSleep *PriorityMap::find<ModeSleep>(std::string const&);' 

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


PS: Для тех из вас, интересно, о том, как режимы пути являются найдены на основе их имен - на самом деле я собираюсь изменить find() вернуть вектор ссылок, поскольку имена не являются уникальными мой случай, и я могу иметь режимы с тем же именем в разных местах моей карты приоритетов. Прямо сейчас find() возвращает первое совпадение, но должно быть достаточно для цели этого сообщения.

ответ

1

Вам нужно определить функцию PriorityMap::find в вашем файле заголовка, а не в файле cpp.

Проблема заключается в том, что при использовании функций шаблона в данном компиляционном модуле не создается никаких экземпляров, если фактическое использование функции не используется в указанном устройстве. Единица компиляции, в которой вы определяет, ваша функция не является той, в которой вы на самом деле используете, поэтому в вашем случае никаких экземпляров не создается. Позже, когда дело доходит до связывания блока (ов) компиляции, в котором функция используется, определения не найдено, поэтому вы получаете ошибку компоновщика.

Если вы хотите избежать определения функции в файле заголовка, вы можете явно создать экземпляр в файле cpp, в котором он определен. Например:

template void PriorityMap::find(MyClass); 

Недостатком является то, что этот каст файл должен знать обо всех типах, которые когда-либо должны быть использованы с PriorityMap::find

+0

* Facepalm * Я совершенно забыл, что шаблоны должны быть в заголовки! Как глупо со мной ... Функции члена шаблона - это всего лишь шаблоны. – rbaleksandar