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

Template member function in a non-template class - how to define and call

我最近一直在玩 std::map 并想出了一个宏伟的 :D 设计来创建一个优先级地图 - 一个包含根据优先级分组的各种模式的地图。

我有以下 class 结构:

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;
};

另一方面,我有另一个 class,它使用 Mode,称为 PriorityMap,具有以下 class 定义:

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() 几次之后,我遇到了一个问题,我不得不手动 downcast 将 returned 指针指向Mode 的各自推导(在我的例子中只是 ModeSleepModeFactorial)。所以我决定向该函数添加模板功能并在调用它时添加一些反馈会很有用:

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;
    }
  }
}

根据我的定义,您可以看到我的优先级图中的 found 模式基于两个因素:

虽然我的 find() 调用有问题,但我的构建在第一次使用它时中断并出现以下错误:

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

我还没有完成很多模板成员函数,希望对此提供一些反馈。如果您需要更多信息,请告知并提供。


PS:对于那些想知道如何根据名称 找到 模式的人 - 我实际上要更改我的 find() 到 return 一个引用向量,因为在我的情况下名称不是唯一的,我可以在优先级映射的不同位置使用相同名称的模式。现在 find() return 是第一场比赛,但应该足以满足 post.

的目的

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

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

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

template void PriorityMap::find(MyClass);

这里的缺点是这个 cpp 文件必须知道所有必须与 PriorityMap::find

一起使用的类型