Контейнер C++ STL для базового класса шаблона

Я пытаюсь сохранить объекты, полученные из шаблонного базового класса, на карте 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)));  

person greenback    schedule 07.02.2011    source источник
comment
Вы имеете в виду класс CDerivedString: public CBase‹string›?   -  person SCFrench    schedule 07.02.2011
comment
Я отредактировал, чтобы показать угловые скобки, которые были скрыты, но кажется, что в сообщении об ошибке все еще чего-то не хватает (обратите внимание на пробел) из того, что было сказано изначально.   -  person Fred Nurk    schedule 07.02.2011
comment
Вы имеете в виду класс CDerivedString: public CBase‹string›? Да, я исправил приведенный выше код, полученный из-за неаккуратной операции вырезания и вставки. Я не думаю, что сейчас что-то пропало.   -  person greenback    schedule 07.02.2011
comment
Большое спасибо за ответы. Я попробую предложение Кейта определить полностью общую базу и использовать интеллектуальную карту ptr из Boost, чтобы содержать указатели на базовый класс.   -  person greenback    schedule 07.02.2011


Ответы (3)


Следующая строка:

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;
person Keith    schedule 07.02.2011
comment
Хотя вы этого не показываете, это может привести к тому, что необработанные указатели, владеющие своими ресурсами, будут храниться в карте, что приведет к утечкам. Используйте интеллектуальный контейнер (например, из Boost) или контейнер интеллектуальных указателей. - person Fred Nurk; 07.02.2011
comment
Спасибо за ответы. Именно такой подход я и имел в виду. Когда вы говорите, что указатели владеют своими ресурсами, я подумал, в свою очередь, что карта владеет указателями, поэтому при разрушении, пока все деструкторы в объектах, на которые указывают, являются виртуальными, ничего не должно утечь. Если я использую смарт-контейнер (например, boost::ptr_map) вместо std::map, как вы думаете, должен ли описанный выше подход работать? Спасибо еще раз! - person greenback; 07.02.2011
comment
'карта владеет указателями'; это буквально верно, но стирается только хранилище, используемое для указателей, а не объект, на который он указывает. Итак, да, используйте boost::ptr_map или что-то подобное, если вы хотите, чтобы карта владела объектами, на которые указывает. Подробности смотрите в других ответах. - person Keith; 08.02.2011

Используйте контейнеры указателей Boost, которые точно обеспечивают «карта, специализированная для указателя на базовый класс», которую вы пробовали:

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

Поскольку вы используете T в интерфейсе CBase, кажется, что вы хотите сохранить CBase в качестве шаблона, но имейте в виду, что в этом случае нет общего базового класса между классами, производными от CBase‹int> и CBase‹string>, поскольку они два разных типа, и вы не можете хранить классы, производные от любого из них, в одном контейнере.

person Fred Nurk    schedule 07.02.2011

Вам нужно иметь базовый класс для хранения в std::map; это должен быть либо класс без шаблона, либо конкретный экземпляр шаблона. Невозможно сохранить «любой экземпляр CBase», если нет общего базового класса для всех экземпляров. Вы также можете рассмотреть возможность использования boost::shared_ptr или std::shared_ptr для автоматического управления временем жизни ваших объектов.

person Jeremiah Willcock    schedule 07.02.2011