Что такое множественное повторное наследование?

Я называю следующее «множественное повторное наследование»:

  • наследование класса один раз напрямую и один или несколько раз косвенно путем наследования одного или нескольких его потомков
  • наследование класса косвенно два или более раз путем наследования двух или более его потомков

Я хочу знать, существует ли он и как однозначно получить доступ к встроенным подобъектам.

1.) [Профессиональный C ++, 2й ред.] утверждает, что скомпилируемая программа не может иметь класс, который напрямую наследует как ее непосредственного родителя, так и родительский класс указанного родителя. Это правда?

Учитывая GrandParent а также Parent, который расширяет GrandParent, VC12 и G ++ позволяет GrandChild напрямую наследовать от обоих Parent а также GrandParent, В VC12 и g ++ эти классы можно определить следующим образом:

GrandParent объявляет int num элемент данных. Parent объявляет свое num в дополнение к наследованию GrandParent«s num, GrandChild объявляет свое num в дополнение к наследованию Parentи GrandParent«s nums.

VC12, по-видимому, допускает однозначный доступ к элементам по всем направлениям, но g ++ допускает его только в некоторых случаях.

#include <iostream>
using std::cout;
using std::endl;

struct GrandParent { int num; };
struct Parent : GrandParent { int num; };
struct GrandChild : GrandParent, Parent { int num; };

int main()
{
GrandChild gc;
gc.num = 2;
gc.Parent::num = 1;
gc.Parent::GrandParent::num = 0; // g++ error: ‘GrandParent’ is an ambiguous base of ‘GrandChild’
gc.GrandParent::num = 5;         // g++ error: ‘GrandParent’ is an ambiguous base of ‘GrandChild’

// --VC12 output; g++ output--
cout << gc.num << endl;                      // 2 ; 2
cout << gc.Parent::num << endl;              // 1 ; 1
cout << gc.Parent::GrandParent::num << endl; // 0 ; N/A due to above error
cout << gc.GrandParent::num << endl;         // 5 ; N/A due to above error
}

2.) Почему (а) gc.Parent::GrandParent::num неоднозначно в g ++, когда (б) gc.Parent::num нет? (а) однозначно описывает его местоположение в дереве наследования. gc только 1 Parent подобъект, который имеет только 1 GrandParent подобъект, который имеет только 1 num, Для (б), gc имеет один Parent, который имеет свой num но также GrandParent подобъект с другим num,

3.) Для gc.GrandParent::numВроде VC12 смотрит в gcСразу GrandParent базовый подобъект для последнего num, Я предполагаю, причина этого однозначна в том, что это поиск имени, gcТаким образом, сущность справа от . ищется первым в gcи самые непосредственные GrandParent в gcсфера действия напрямую наследуется, а не косвенно наследуется через Parent, Я ошибся?

4.) Почему gc.GrandParent::num неоднозначно для g ++, когда gc.Parent::num нет? Если человек неоднозначен, то не должны ли оба быть одинаково неоднозначными? Для предварительного, gc имеет два GrandParents; и для последнего, Parent имеет 2 nums.


Gregoire, Marc R. et al. Профессиональный C ++, 2й редактор Индианаполис, IN: Wiley Publishing, 2011. с. 241. Печать.

14

Решение

Общий термин для этого ромбовидный узор (или же алмазная проблема).

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

Один из обходных путей — сделать базу непрямой. Новая особенность конструкторов наследования в C ++ 11 позволяет создавать идеальные оболочки:

template< typename base, typename tag >
struct disambiguated_base : base
{ using base::base; };

Учитывая неиспользуемый тип тега, он генерирует новый класс, производный и функционально идентичный данной базе. Тип тега может быть неполным классом, обозначаемым подробным спецификатором типа:

struct GrandChild : Parent,
disambiguated_base< GrandParent, class grandchild_grandparent_tag > {

typedef disambiguated_base< GrandParent, grandchild_grandparent_tag >
my_direct_grandparent;

int num;
};

Сейчас GrandChild можешь использовать my_direct_grandparent:: для устранения неоднозначности доступа членов.

3

Другие решения

Я добавляю к принятому ответу. В нем говорится, что производный класс не может получить доступ к прямому base класс, если производный класс также косвенно наследует base, Его решение делает base косвенный класс, обернув его шаблоном, аргумент второго типа которого tag, Это обеспечивает base является косвенным по отношению к производному классу при условии, что производный класс расширяет упакованную базу уникальным tag, В приведенном ниже примере будет использоваться не тип tag,

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

  • яго класс наследует от base и (я — 1)го,
  • (я — 1)го наследуется от base и (я — 2)го,
  • …, а также
  • 2й наследуется от base,

тогда это импровизированный контейнер, где каждый элемент хранится в каждом уникально помеченном base, В таком случае, tagИзготовление должно быть автоматизировано. Одним из способов является объединение всех производных классов с помощью нетипового шаблона. Свой нетипичный параметр N Можно указать количество рекурсивных итераций наследования. Делая tag это не типовой параметр, значение параметра, определяющего количество подклассов, может быть однозначно связано со значением одного тега для каждого типа подобъекта. Например, tag = 10 соответствует N = 10, который относится к 10-му поколению по иерархии:

// disambiguated_wrapper.h

struct int_wrapper {
int num;
};

template < typename base, unsigned int tag >
struct disambiguated_wrapper : base {
using base::base;
};

//impvised_container.h

#include "disambiguated_wrapper.h"
template <unsigned int N>
struct improvised_container :
protected disambiguated_wrapper<int_wrapper, N>,
protected improvised_container<N - 1> {

unsigned int size() const { return N; }

int& at(const unsigned int index) {
if (index >= N) throw "out of range";
else return (index == N - 1) ?
this->disambiguated_wrapper<int_wrapper, N>::num :
this->helper(index);
}
protected:
int& helper(const unsigned int index) {
return (index == N - 1) ?
this->disambiguated_wrapper<int_wrapper, N>::num :
this->improvised_container<N - 1>::helper(index);
}
};
#include "specializations.h"

// specializations.h

template <>
struct improvised_container<0> {
improvised_container() = delete;
}; // ^ prohibits 0-length container

template <>
struct improvised_container<1> :
protected disambiguated_wrapper<int_wrapper, 1> {

unsigned int size() const { return 1; }

int& at(const unsigned int index) {
if (index != 0) throw "out of range";
else return this->disambiguated_wrapper<int_wrapper, 1>::num;
}
protected:
int& helper(const unsigned int index) {
if (index != 0) throw "out of range";
else return this->disambiguated_wrapper<int_wrapper, 1>::num;
}
};

// main.cpp

#include "improvised_container.h"#include <iostream>
int main() {
improvised_container<10> my_container;
for (unsigned int i = 0; i < my_container.size(); ++i) {
my_container.at(i) = i;
std::cout << my_container.at(i) << ",";
}   // ^ Output: "0,1,2,3,4,5,6,7,8,9,"}

Элемент доступа at не может уменьшить index рекурсивно называть себя, потому что index не является константой времени компиляции. Но N является. Так, at звонки helper, который рекурсивно вызывает (i — 1)го версия себя в (я — 1)го подобъект, убывающий N пока это не будет равно index – 1при каждом вызове перемещение на одну область глубже и, наконец, возвращение элемента целевой области. Это проверяет против index – 1 и не indexпотому что 0го improvised_container Ctor специализации deleteд. at компенсирует офф-по-одному.

improvised_container использования protected наследование, чтобы предотвратить доступ клиентского кода к at а также size методы его базовых подобъектов. Размер подобъекта меньше размера вложенного объекта.

Это работает в g ​​++ 4.8. Наследующий конструктор using base::base вызывает ошибки в VC12, но это может быть опущено, потому что тип элемента int,

1

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector