【问题标题】:C++ STL container for template base class模板基类的 C++ STL 容器
【发布时间】:2011-06-22 11:49:12
【问题描述】:

我正在尝试将派生自模板化基类的对象存储在 STL 映射中。但是,尝试插入派生(或实际上是基)对象会返回:

C2440 'initializing' : cannot convert from 'CBase<T> ' to 'CBase<T>'

我知道使用派生类是使 STL 容器异构化的一种公认方法 (http://www.parashift.com/c++-faq-lite/containers.html#faq-34.4)。我想知道在这种情况下是否可以使用模板。这将非常方便,因为我可以在基类中对一系列容器进行单一声明,这些容器在编译时为我的各种类型实例化,而不是在非模板派生类中重复声明。

我的代码如下:

//Header
using namespace std;

template<class T>
class CBase
{
    public:
        CBase::CBase() {};
        virtual CBase::~CBase() {};
        vector<pair<int, T> > RetrieveVect() { return vect; };

    private:
        vector<pair<int, T> > vect;
};

class CDerivedString : public CBase<string>
{
    ...
};

class CDerivedInt : public CBase<int>
{
    ...
};

//cpp
int main(void)
{
    //Map specialised for pointer to base class
    map<string, CBase<class T>* > m_myMap;

    string s = "key";

    //Create and insert object (base class)
    CBase<int> *dataInt = new CBase();
    //The following results in error C2440: 'initializing' : cannot convert from 'CBase<T> ' to 'CBase<T>
    m_myMap.insert(std::make_pair(s, dataInt));

    //Create and insert object (derived class)
    CBase<int> *dataBase = new CBase<int>();
    //The following results in error C2440: 'initializing' : cannot convert from 'CBase<T> ' to 'CBase<T>
    m_myMap.insert(pair<string, CBase<class T>* >(s, static_cast<CBase*>(dataInt)));
}

我尝试对派生类指针进行 dynamic_cast 以将其转换为基指针类型,但这也不起作用:

//error C2440: 'static_cast' : cannot convert from 'CBase<T> *' to 'CBase<T> *'
m_myMap.insert(pair<string, CBase<class T>* >(s, static_cast<CBase<class T>*>(dataInt)));  

【问题讨论】:

  • 你的意思是“class CDerivedString : public CBase”吗?
  • 我已经编辑显示隐藏的尖括号,但似乎错误消息仍然缺少最初所说的内容(注意空格)。
  • 你的意思是“class CDerivedString : public CBase”吗?是的 - 我已经更正了上面的代码,它来自一个混乱的剪切和粘贴操作。我认为现在没有任何遗漏。
  • 非常感谢您的回复。我将尝试 Keith 的建议,即定义一个完全通用的基类,并使用 Boost 中的智能 ptr 映射来包含指向基类的指针。

标签: c++ templates base-class


【解决方案1】:

下面一行:

map<string, CBase<class T>* > m_myMap;

几乎可以肯定并不意味着你认为它是什么。这相当于:

map<string, CBase<T>* > m_myMap;

即:'T' 是一个具体的类,而不是模板参数。类之间当然没有关系:

CBase<int> 

和

CBase<T>

因此出现错误消息 - 您从未定义(或打算)具体类“T”。使用正确的基础获取 SCFrench 的评论,然后在地图中使用它:

map<string, CBase<int>* > m_myIntMap;

将允许您存储具体的 CDerivedInt* 对象。如果要存储任何对象,请定义一个完全通用的基础:

 class CBaseAbc 
     { 
        virtual ~CBaseAbc() = 0;
     };
 template<class T>
 class CBase : public CBaseAbc 
    {
     // etc.
    };

map<string, CBaseAbc* > m_myAnthingMap;

【讨论】:

  • 尽管您没有显示它,但这可能会导致“拥有”其资源的原始指针存储在映射中,这将导致泄漏。使用智能容器(例如来自 Boost)或智能指针容器。
  • 感谢您的回复。这是我想到的那种方法。当您说指针“拥有”它们的资源时,我认为反过来映射“拥有”指针,因此在被破坏时,只要指向对象中的析构函数都是虚拟的,就不会泄露任何东西。如果我使用智能容器(例如 boost::ptr_map)而不是 std::map,你认为上面的方法应该有效吗?再次感谢!
  • '地图“拥有”指针';这确实是真的,但只有用于指针的存储被擦除,而不是指向的对象。所以,是的,如果您希望地图拥有指向的对象,请使用 boost::ptr_map 等。有关详细信息,请参阅其他答案。
【解决方案2】:

使用Boost's Pointer Containers,它恰好提供了您尝试过的“专门用于指向基类的指针的映射”:

// Use whichever is appropriate since you've written CBase as a template:
boost::ptr_map<string, CBase<int> > m_myMap;
boost::ptr_map<string, CBase<string> > m_myMap;

// If CBase were instead not a template base class:
boost::ptr_map<string, CBase> m_myMap;

由于您在 CBase 的接口中使用了 T,看来您希望将 CBase 保留为模板,但请注意,在这种情况下,从 CBase 和 CBase 派生的类之间没有公共基类,因为这是两种不同的类型,您不能将派生自其中任何一种的类存储在一个容器中。

【讨论】:

    【解决方案3】:

    你需要有一个基类来存储在std::map;它需要是非模板类或模板的特定实例。除非所有实例都有一个公共基类,否则不可能存储“CBase 的任何实例”。您可能还想考虑使用boost::shared_ptr 或std::shared_ptr 来自动管理对象的生命周期。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-04-10
      • 1970-01-01
      • 2019-04-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多