我最近一直在和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()
返回第一场比赛但是应该足以满足这篇文章的目的。
答案 0 :(得分:1)
您需要在头文件中定义函数PriorityMap::find
而不是cpp文件。
问题在于,对于模板函数,除非在所述单元中实际使用了函数实例化,否则不会在给定的编译单元中创建实例化。您定义函数的编译单元与您实际使用的函数不同,因此在您的情况下,实际上不会创建任何实例化。稍后,当链接使用函数的编译单元时,不会找到定义,因此会出现链接器错误。
如果要避免在头文件中定义函数,则可以在定义它的cpp文件中显式实例化它。例如:
template void PriorityMap::find(MyClass);
这里的缺点是这个cpp文件必须知道将要与PriorityMap::find
一起使用的所有类型