Инициализация типов элементов базового класса шаблонов в списках инициализаторов производного класса

Вот какой код, излагающий проблему, с которой я боролся. Последняя проблема (в настоящий момент касается g++) заключается в том, что: "ошибка:" Foo-T "не была объявлена ​​в этой области" при выполнении процедуры конструктора Bar:: Bar (...). В противном случае проблема, с которой я пытаюсь научиться, - это установить типы членов базового класса на основе аргументов, переданных конструктору производного класса с использованием шаблонов. Если бы был способ установить тип элемента базового класса (T Foo-T), просто передав аргументы конструктору производного класса, я бы предпочел это. На данный момент я не вижу пути использования аргумента шаблона и соответствующего аргумента конструктора производного класса для выполнения этой задачи. Можете ли вы заметить что-либо в следующем коде, что я могу сделать лучше для достижения тех же целей? Я новичок в родовых кодировках и шаблонах.

#include <iostream>
typedef int a_arg_t;
typedef double b_arg_t;
typedef std::string foo_arg_t;

class TypeA {
public:
    TypeA ();
    TypeA (a_arg_t a) {
        /* Do sosmething with the parameter passed in */
    }

};

class TypeB {
public:
    TypeB ();
    TypeB (b_arg_t b) {
        /* typeB constructor - do something here */
    }

};

// The base-class with a member-type to be determined by the template argument
template <class T>
class Foo {

public:
    Foo (const foo_arg_t foo_arg) : _foo_arg(foo_arg)       // initialize something here
    {
        /* do something for foo */
    }
    T Foo_T;        // either a TypeA or a TypeB - TBD
    foo_arg_t _foo_arg;
};

// the derived class that should set the basse-member type (T Foo_T)
template <class T>
class Bar : public Foo<T> {
public:
    Bar (const foo_arg_t bar_arg, const a_arg_t a_arg)
    : Foo<T>(bar_arg)   // base-class initializer
    {
        // the initialization of Foo_T has to be done outside the initializer list because it not in scsope until here
        Foo_T = TypeA(a_arg);   // if an a_arg_t is passed in, then we set the Foo_T to TypeA, etc.
    }

    Bar (const foo_arg_t bar_arg, const b_arg_t b_arg)
    : Foo<T>(bar_arg)
    {
        Foo_T = TypeB(b_arg);
    }

};

int main () {

    b_arg_t b_arg;
    a_arg_t a_arg;
    foo_arg_t bar_arg;

    Bar<TypeA> a (bar_arg, a_arg);  // try creating the derived class using TypeA
    Bar<TypeB> b (bar_arg, b_arg); // and another type for show

return 0;
}

Ответ 1

Тип Foo_T не будет искать в базовом классе при использовании в конструкторе производных (Bar).

Bar (const foo_arg_t bar_arg, const a_arg_t a_arg)
: Foo<T>(bar_arg)   // base-class initializer
{
    Foo_T = TypeA(a_arg);   TypeA, etc. // Won't compile, per the standard
}

Это соответствует стандарту С++, в котором говорится, что неквалифицированные имена обычно не зависят, и их следует искать, когда шаблон полностью определен.

Так как определение базового класса шаблона в это время неизвестно (могут быть полностью специализированные экземпляры шаблона, которые вытаскиваются позже в блоке компиляции), неквалифицированные имена никогда не разрешаются именам в зависимых базовых классах.

Если вам требуется имя из базового класса, когда задействованы шаблоны, вы должны либо полностью квалифицировать их, либо сделать их неявно зависимыми в производном классе.

 Foo< T >::Foo_T = TypeA(a_arg);   // fully qualified will compile

или, сделайте его зависимым

 this->Foo_T = TypeA(a_arg);

Поскольку параметр this делает его зависимым от шаблона, разрешение типа откладывается до "фазы 2" экземпляра шаблона (а затем базовый класс также полностью известен)

Обратите внимание, что если вы хотите использовать функцию из базового класса, вы могли бы также добавить декларацию использования.

(внутри Bar())

  some_foo_func(); // wouldn't work either

  using Foo<T>::some_foo_func;
  some_foo_func(); // would work however

Ответ 2

Извините, что не бесполезен, но я также не вижу пути вокруг этого, не делая точно, что вы заявили:

На данный момент я не вижу пути использования как аргумент шаблона, так и сопоставление конструктора производного класса аргумент для выполнения этой задачи.

Вам, вероятно, придется немного поразмышлять:

template<>
Bar<TypeA>::Bar (const foo_arg_t bar_arg, const a_arg_t a_arg)
: Foo<TypeA>(bar_arg)   // base-class initializer
{
    // the initialization of Foo_T has to be done outside the initializer list because it not in scsope until here
    Foo_T = TypeA(a_arg);   // if an a_arg_t is passed in, then we set the Foo_T to TypeA, etc.
}

template< class T>
Bar<T>::Bar (const foo_arg_t bar_arg, const a_arg_t a_arg)
: Foo<T>(bar_arg)   // base-class initializer
{
    // Throw exception?
}

template<>
Bar<TypeB>::Bar (const foo_arg_t bar_arg, const b_arg_t b_arg)
: Foo<TypeB>(bar_arg)
{
    Foo_T = TypeB(b_arg);
}

template< class T >
Bar<T>::Bar (const foo_arg_t bar_arg, const b_arg_t b_arg)
: Foo<T>(bar_arg)
{
    // Throw exception ?
}

К сожалению, у меня нет доступа к компилятору на данный момент, чтобы проверить этот код, поэтому будьте осторожны.


В ответ на ваш вопрос/комментарий. У меня было следующее для компиляции:

#include <iostream>
typedef int a_arg_t;
typedef double b_arg_t;
typedef std::string foo_arg_t;

class TypeA {
public:
  TypeA () {}
  TypeA (a_arg_t a) {}
};

class TypeB {
public:
  TypeB () {}
  TypeB (b_arg_t b) {}
};

template <class T>
class Foo {
public:
  Foo (const foo_arg_t foo_arg) : _foo_arg(foo_arg) {}
  T Foo_T;        // either a TypeA or a TypeB - TBD
  foo_arg_t _foo_arg;
};

// the derived class that should set the basse-member type (T Foo_T)
template <class T>
class Bar : public Foo<T> {
public:
  Bar (const foo_arg_t bar_arg, const a_arg_t a_arg)
  : Foo<T>(bar_arg)   // base-class initializer
  {
    Foo<T>::Foo_T = TypeA(a_arg);
  }

  Bar (const foo_arg_t bar_arg, const b_arg_t b_arg)
  : Foo<T>(bar_arg)
  {
    Foo<T>::Foo_T = TypeB(b_arg);
  }
};

int main () {
  b_arg_t b_arg;
  a_arg_t a_arg;
  foo_arg_t bar_arg;

  Bar<TypeA> a (bar_arg, a_arg);  // try creating the derived class using TypeA
  Bar<TypeB> b (bar_arg, b_arg); // and another type for show

  return 0;
}

Ответ 3

Когда я впервые посмотрел на ваш код, я был абсолютно уверен, что вам придется решить проблему с частичной специализацией. В самом деле, это может быть все же так, но я уменьшил количество кода, необходимого для репликации вашей ошибки, и заметил, что ошибка возникает только при компиляции с gcc (я не знаю, какая версия компилятора работает в моем университете, хотя), а при компиляции с Visual Studio 2003 - everythying счастлив.

Ниже воспроизводится код ошибки, но незначительное, казалось бы, невиновное изменение удивительно устранит его:

template <typename T>
class ClassA
{
public:
    ClassA () {}
    T vA;
};


template<typename T>
class ClassB : public ClassA<T>
{
public:
    ClassB ()
    {
        vA = 6;
    }
};

int main ()
{
    ClassB<int> cb;
}

Теперь, если вы удалите объявление шаблона из ClassB и получите его непосредственно наследовать от ClassA:

class ClassB : public ClassA<int>
{
public:
    ClassB ()
    {
        vA = 6;
    }
};

а затем измените объявление cb на соответствие

    ClassB cb;

Тогда ошибка исчезает, хотя явно нет ничего относительно сферы действия vA (или в вашем случае Foo_T)

Я предполагаю, что это ошибка компилятора, и задаюсь вопросом, может ли, пожалуй, полностью обновить gcc-компилятор, все еще будет возникать проблема.