Наследование от вложенных структур: шаблоны и указатели - PullRequest
2 голосов
/ 04 сентября 2011

Я пытаюсь добавить некоторые дополнительные поля во вложенную структуру в 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**’

Может ли кто-нибудь помочь мне понять

  1. Является ли это правильным способоми если есть лучший способ, и

  2. Почему компилятор доволен методами 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 ]

2 голосов
/ 04 сентября 2011

Проблема не во вложенных структурах или шаблонах, а в указателях на указатели и наследование:

  • Base * может содержать экземпляр Derived *, поэтому Base * может быть приведен кПроизводное *.
  • Base ** не может содержать экземпляр Derived ** и поэтому не может быть преобразовано в Derived **.

Если Base ** можетудерживая массив Derived, вы могли бы сделать следующее:

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!

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

1 голос
/ 04 сентября 2011

Чтобы ответить на ваш второй вопрос:

Если вы переопределите виртуальную функцию, подписи должны совпадать.Единственное исключение из этого правила состоит в том, что если в базовом классе функция возвращает указатель или ссылку на некоторый класс B, то переопределяющий метод может возвращать указатель или ссылку на тип D, где D получено из B(это ковариация возвращаемого типа).Сказав это, должно быть ясно, почему ваш GetN работает - DNode является производным от Node, а функция базового класса возвращает Node*, а переопределитель возвращает DNode*.

Теперь давайтепосмотрите на GetNAddr.Метод базового класса возвращает Node** или pointer to Node*.Вы можете изменить этот тип возврата в переопределяющей функции в производном классе , если бы он возвращал что-то, производное от узла *.Но это естественно невозможно, так как указатель не может иметь производные классы.DNode* не является производным от Node* - следовательно, компилятор жалуется

...