Наследование от вложенных структур: шаблоны и указатели
Я пытаюсь добавить некоторые дополнительные поля во вложенную структуру в C++, и дизайн диктует, что я хочу сделать это через наследование. Я получаю сообщение об ошибке, которое, как ни странно, зависит от того, работаю ли я с типом T* или типом T**. Я довольно смущен и был бы признателен, если бы кто-нибудь помог мне понять, что здесь происходит.
Вложенная структура - Base::Node, и я хочу добавить поле b в Base::Node, а затем использовать Derived, как показано в основном. Когда я устанавливаю #define сверху 0, все компилируется и работает нормально. Когда я изменяю #define на 1, я получаю следующую ошибку компилятора:
main_inhtest.cpp: In instantiation of ‘Derived<int>’:
main_inhtest.cpp:52: instantiated from here
main_inhtest.cpp:44: error: conflicting return type specified for ‘Derived<T>::DNode** Derived<T>::GetNAddr() [with T = int]’
main_inhtest.cpp:24: error: overriding ‘Base<T>::Node** Base<T>::GetNAddr() [with T = int]’
main_inhtest.cpp: In member function ‘Derived<T>::DNode** Derived<T>::GetNAddr() [with T = int]’:
main_inhtest.cpp:57: instantiated from here
main_inhtest.cpp:44: error: invalid static_cast from type ‘Base<int>::Node**’ to type ‘Derived<int>::DNode**’
Может ли кто-нибудь помочь мне понять
Является ли это правильным способом сделать это, и если есть лучший способ, и
Почему компилятор доволен методами GetN(), но не методами GetNAddr()?
Спасибо!
#include <iostream>
#define TRY_GET_N_ADDR 1
template <typename T> class Base {
public:
Base() { n = new Node(); }
struct Node
{
T a;
};
virtual Node *GetN() { return n; }
virtual Node **GetNAddr() { return &n; }
Node *n;
};
template <typename T> class Derived : public Base<T> {
public:
Derived() { Base<T>::n = new DNode(); }
struct DNode : Base<T>::Node
{
T b;
};
// This method is fine
DNode *GetN() { return static_cast<DNode *>(Base<T>::GetN()); }
#if TRY_GET_N_ADDR
// Compiler error here
DNode **GetNAddr() { return static_cast<DNode **>(Base<T>::GetNAddr()); }
#endif
};
int main (int argc, const char * argv[]) {
Derived<int> d;
d.GetN()->a = 1;
d.GetN()->b = 2;
std::cout << d.GetN()->a << " " << d.GetN()->b << std::endl;
}
2 ответа
Проблема не в вложенных структурах или шаблонах, а в указателях на указатели и наследование:
- Base* может содержать экземпляр Derived *, и поэтому Base* может быть понижен до Derived*.
- База ** не может содержать экземпляр Derived ** и поэтому не может быть понижена до Derived**.
Если Base** может содержать массив Derived's, вы могли бы сделать следующее:
Derived* pDerived;
Derived** ppDerived = &pDerived;
Base** ppBase = ppDerived; // not allowed in real world
*ppBase = new Base; // should be safe, right?
pDerived->derivedFunc(); // invoked on instance of Base!
Последняя строка привела бы к некоторой произвольной ошибке. Поэтому такого рода назначение не допускается.
Чтобы ответить на ваш второй вопрос:
Если вы переопределите виртуальную функцию, подписи должны совпадать. Единственное исключение из этого правила состоит в том, что если в базовом классе функция возвращает указатель или ссылку на некоторый класс B
тогда переопределяющий метод может возвращать указатель или ссылку на тип D
где D
является производным от B (это ковариация типа возвращаемого значения). Сказав это, должно быть ясно, почему ваш GetN
работает - DNode
происходит от Node
и функция базового класса возвращает Node*
и переопределитель возвращается DNode*
,
Теперь давайте посмотрим на GetNAddr
, Метод базового класса возвращает Node**
или же pointer to Node*
, Вы можете изменить этот тип возврата в переопределяющей функции в производном классе, если бы он возвращал что-то, производное от Node*. Но это, естественно, невозможно, поскольку указатель не может иметь производных классов. DNode*
не является производным от Node*
- следовательно компилятор жалуется