非模板类中的模板成员函数 - 如何定义和调用

时间:2016-06-01 08:32:20

标签: c++ templates member-functions

我最近一直在和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);
};

下面您可以看到如何初始化和调用对象的示例:

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()移动template版本find())。我的初始设计(现在_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的相应派生的指针(在我的情况下只是{ {1}}和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()

我没有做很多模板成员函数,并希望得到一些反馈。如果您需要更多信息,请告诉并提供。

PS:对于那些想知道找到模式的方式的人 - 我实际上要改变我的In function `main': undefined reference to `ModeSleep *PriorityMap::find<ModeSleep>(std::string const&);' 以返回引用的向量在我的情况下,名称不是唯一的,我可以在优先级映射的不同位置使用相同名称的模式。现在find()返回第一场比赛但是应该足以满足这篇文章的目的。

1 个答案:

答案 0 :(得分:1)

您需要在头文件中定义函数PriorityMap::find而不是cpp文件。

问题在于,对于模板函数,除非在所述单元中实际使用了函数实例化,否则不会在给定的编译单元中创建实例化。您定义函数的编译单元与您实际使用的函数不同,因此在您的情况下,实际上不会创建任何实例化。稍后,当链接使用函数的编译单元时,不会找到定义,因此会出现链接器错误。

如果要避免在头文件中定义函数,则可以在定义它的cpp文件中显式实例化它。例如:

template void PriorityMap::find(MyClass);

这里的缺点是这个cpp文件必须知道将要与PriorityMap::find一起使用的所有类型