Непонятная ошибка с шаблонами

Рейтинг: 0Ответов: 1Опубликовано: 22.03.2015

Необходимо создать несколько классов контейнеров. Общие функции я решил вынести в базовый класс Base. Также необходима возможность конвертирования от одного типа содержащегося объекта к другому, я решил это сделать с помощью copy-initialization. Код:

template <typename TObj, template<typename T> class TDerived>
class Base
{
    public:
    Base(TObj *o)
    {
    }
    template<typename TAnotherObj>
    TDerived<TAnotherObj> cast()
    {
        //stub
        return TDerived<TAnotherObj>(0);
    }
};

template <typename TObj>
class Container: public Base<TObj, Container>
{
    public:
    Container(TObj *o):Base<TObj, Container>(o){}

    template<typename TAnotherObj>
    Container(Container<TAnotherObj> container2):Base<TObj, Container>(container2.cast<TObj>())
    {       
    }
};

class Obj1{};

class Obj2{};

int main(){
    Container<Obj1> container1(new Obj1());
    (Container<Obj2>) container1;
}

Однако возникает непонятная ошибка:

In constructor «Container<TObj>::Container(Container<TAnotherObj>)»:
ошибка: expected primary-expression before «>» token
Container(Container<TAnotherObj> container2):Base<TObj, Container>(container2.cast<TObj>())

ошибка: expected primary-expression before «)» token
Container(Container<TAnotherObj> container2):Base<TObj, Container>(container2.cast<TObj>())

Если убрать <TObj>, то есть оставить просто container2.cast(), то будет другая ошибка:

ошибка: нет подходящей функции для вызова «Container<Obj1>::cast()»
  Container(Container<TAnotherObj> container2):Base<TObj, Container>(container2.cast())

замечание: candidate is:
замечание: template<class TAnotherObj> TDerived<TAnotherObj> Base<TObj, TDerived>::cast() [with TAnotherObj = TAnotherObj; TObj = Obj1; TDerived = Container]
  TDerived<TAnotherObj> cast()

замечание:   template argument deduction/substitution failed:
замечание:   couldn't deduce template parameter «TAnotherObj»
  Container(Container<TAnotherObj> container2):Base<TObj, Container>(container2.cast())

Почему возникают ошибки и как их исправить?

Ответы

▲ 1Принят

Для решения проблемы нужно было добавить template перед cast:

template<typename TAnotherObj>
Container(Container<TAnotherObj> container2):
    Base<TObj, Container>(container2.template cast<TObj>()){}

Почему так — я сам не очень понял, но примерно можно объяснить так: cast — это «зависимое» имя (зависит от параметров шаблона), поэтому компилятору нужно сказать, что это шаблон метода (а, например, не переменная). В общем, как говорится, «в любой непонятной ситуации добавляй template или typename».

Как правильно заметил @atwice, в базовом классе конструктор принимает аргумент TObj*, а не Container<TObj>. Однако код с приведённым выше исправлением все равно работает, наверное, из-за того, что компилятор использует встроенный конструктор копирования.