Инициализация шаблонных типов элементов базового класса в списках инициализатора производного класса - PullRequest
13 голосов
/ 13 июля 2009

Вот код, описывающий проблему, с которой я боролся. Последняя проблема (что касается 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's 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's 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;
}

Ответы [ 3 ]

15 голосов
/ 13 июля 2009

Тип 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
}

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

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

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

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

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

 this->Foo_T = TypeA(a_arg);

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

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

(внутри бара ())

  some_foo_func(); // wouldn't work either

  using Foo<T>::some_foo_func;
  some_foo_func(); // would work however
3 голосов
/ 13 июля 2009

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

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

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

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's 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;
}
0 голосов
/ 13 июля 2009

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

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

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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...