Доступ производного класса шаблона к данным-членам базового класса
-
13-09-2019 - |
Вопрос
Этот вопрос является развитием вопроса, заданного в эта тема.
Используя следующие определения классов:
template <class T>
class Foo {
public:
Foo (const foo_arg_t foo_arg) : _foo_arg(foo_arg)
{
/* do something for foo */
}
T Foo_T; // either a TypeA or a TypeB - TBD
foo_arg_t _foo_arg;
};
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 = T(a_arg);
}
Bar (const foo_arg_t bar_arg, const b_arg_t b_arg)
: Foo<T>(bar_arg)
{
Foo<T>::Foo_T = T(b_arg);
}
void BarFunc ();
};
template <class T>
void Bar<T>::BarFunc () {
std::cout << _foo_arg << std::endl; // This doesn't work - compiler error is: error: ‘_foo_arg’ was not declared in this scope
std::cout << Bar<T>::_foo_arg << std::endl; // This works!
}
При доступе к членам базового класса шаблонного класса кажется, что я всегда должен явно определять члены, используя синтаксис шаблонного стиля: Bar<T>::_foo_arg
.Есть ли способ избежать этого?Может ли оператор/директива «using» использоваться в методе класса шаблона для упрощения кода?
Редактировать:
Проблема с областью действия решается путем указания переменной синтаксиса this->.
Решение
Вы можете использовать this->
чтобы было ясно, что вы имеете в виду члена класса:
void Bar<T>::BarFunc () {
std::cout << this->_foo_arg << std::endl;
}
Альтернативно вы также можете использовать "using
"в методе:
void Bar<T>::BarFunc () {
using Bar<T>::_foo_arg; // Might not work in g++, IIRC
std::cout << _foo_arg << std::endl;
}
Это дает понять компилятору, что имя члена зависит от параметров шаблона, и он ищет определение этого имени в нужных местах.Для получения дополнительной информации также см. эта запись в C++ Faq Lite.
Другие советы
Здесь базовый класс не является независимым базовым классом (что означает класс с полным типом, который можно определить, не зная аргументов шаблона), и _foo_arg
является независимым именем.Стандарт C++ гласит, что независимые имена не ищутся в зависимых базовых классах.
Для исправления кода достаточно сделать имя _foo_arg
зависимый, поскольку зависимые имена можно искать только во время создания экземпляра, и тогда будет известна точная базовая специализация, которую необходимо изучить.Например:
// solution#1
std::cout << this->_foo_arg << std::endl;
Альтернатива состоит во введении зависимости с использованием квалифицированного имени:
// solution#2
std::cout << Foo<T>::_foo_arg << std::endl;
С этим решением следует быть осторожным, поскольку если для формирования вызова виртуальной функции используется неквалифицированное независимое имя, то квалификация блокирует механизм виртуального вызова и смысл программы меняется.
И вы можете один раз перенести имя из зависимого базового класса в производный класс, using
:
// solution#3
template <class T>
class Bar : public Foo<T> {
public:
...
void BarFunc ();
private:
using Foo<T>::_foo_arg;
};
template <class T>
void Bar<T>::BarFunc () {
std::cout << _foo_arg << std::endl; // works
}
Кажется, работает нормально в Visual C++ 2008.Я добавил несколько фиктивных определений для типов, которые вы упомянули, но не указали источник.Остальное именно так, как вы выразились.Тогда основная функция, чтобы заставить BarFunc
быть создан и вызван.
#include <iostream>
class streamable {};
std::ostream &operator<<(std::ostream &os, streamable &s) { return os; }
class foo_arg_t : public streamable {};
class a_arg_t : public streamable {};
class b_arg_t : public streamable {};
template <class T>
class Foo {
public:
Foo (const foo_arg_t foo_arg) : _foo_arg(foo_arg)
{
/* do something for foo */
}
T Foo_T; // either a TypeA or a TypeB - TBD
foo_arg_t _foo_arg;
};
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 = T(a_arg);
}
Bar (const foo_arg_t bar_arg, const b_arg_t b_arg)
: Foo<T>(bar_arg)
{
Foo<T>::Foo_T = T(b_arg);
}
void BarFunc ();
};
template <class T>
void Bar<T>::BarFunc () {
std::cout << _foo_arg << std::endl;
std::cout << Bar<T>::_foo_arg << std::endl;
}
int main()
{
Bar<a_arg_t> *b = new Bar<a_arg_t>(foo_arg_t(), a_arg_t());
b->BarFunc();
}