Множественное наследование с одним и тем же именем переменной в классах - PullRequest
0 голосов
/ 04 февраля 2019

Я случайно столкнулся с проблемой наличия переменных-членов с одинаковыми именами в классах, используемых в множественном наследовании.Моя основная идея состояла в том, что переменные-члены просто «объединены», то есть происходит множественное объявление.Компилятор даже не предупредил меня, см. MWE ниже.Я понимаю, что иметь переменные с одинаковым именем - плохая идея, поэтому я думаю, что по крайней мере двусмысленно ссылаться на них так, как я делаю;поэтому я ожидал, по крайней мере, предупреждение или, возможно, ошибку.

1) Почему компилятор не выписывает хотя бы предупреждение?

2) Как обработка этих переменных решается внутренне?(Я думаю, псевдонимы, такие как HW :: I и Other :: I, используются, но как они соотносятся с SW1 :: I и SW2 :: I?)

#include <iostream>
struct Other { int I;};
struct HW { int I;};
struct SW1 : Other, HW { int I;};
struct SW2 : HW, Other { int I;};
struct D : SW1 { };
struct E : SW2 { };

int main()
{
    E* e = new E;
    D* d = new D;
    e->I = 3;
    SW1* pc1 = dynamic_cast<SW1*>(d);
    pc1->I = 2;
    std::cerr << d->I;
    std::cerr << e->I;
    SW2* pc2 = dynamic_cast<SW2*>(e);
    pc2->I = 1;
    std::cerr << d->I;
    std::cerr << e->I;
}

Ответы [ 3 ]

0 голосов
/ 04 февраля 2019

Почему компилятор не пишет хотя бы предупреждение?

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

Когда вы пишете выражение доступа к члену класса, например e->I, компилятор не просто ищет имяI, он ищет подобъект , который содержит член, названный таким образом, вместе с членом.Он также начинается с самого производного типа объекта и просматривает субобъект базового класса "вверх", пока не найдет что-то (в двух словах также работает скрытие имени члена в C ++).

Так что дляe->I, он ищет I в E.Этот поиск ничего не находит, поэтому он входит в тему базового класса.Он находит SW2::I, имя, которое ссылается на уникальный член, определенный в SW2.Так что он останавливается.

Если не было SW2::I, он продолжит поиск и найдет и Other::I и HW::I.Теперь одно и то же имя найдено в двух разных подобъектах базового класса, и мы получаем неоднозначность.Не предупреждение о двусмысленности, но совершенно нечеткое выражение e->I, что является ошибкой.

0 голосов
/ 04 февраля 2019

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

При оценке e->I первым найденным кандидатом является I, то естьчлен (через наследование) класса SW2.Члены I, которые SW2 наследуют от его базовых классов, не так хорошо соответствуют, как члены, определенные непосредственно Sw2.

Аналогично, pc1->I однозначно является членом SW1, d->I - то же самое, а pc2->I - однозначно членом базового класса SW2.

Неоднозначностьпроизойдет при оценке e->I, если SW2 не имеет своего собственного члена с именем I (то есть struct SW2: HW, Other {}; (. В этом случае при оценке e->I разрешение имени ищет в SW2 члена с именемI и не находит его. При разрешении имени учитываются два базовых класса HW и Other, каждый из которых имеет член с именем I. Они одинаково хорошо совпадают, поэтому выражение e->I является неоднозначным - и компилятор выдаст диагностику, то есть ошибку (не просто предупреждение). В этом случае программист может явно устранить неоднозначность, используя оператор области действия (::). Например, e->HW::I или e->Other::I, которая полностью определяет имя.

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

0 голосов
/ 04 февраля 2019

Переменные не объединены, вы просто получаете все 3 из них одновременно.Вам нужно привести указатель к нужному типу, чтобы получить доступ к необходимой переменной.

#include <iostream>
struct Other { int I; };
struct HW { int I; };
struct SW1 : public Other, public HW { int I; };
struct D : public SW1 { };

int main() {
    D* d = new D;
    d->I = 1;
    SW1* pc1 = dynamic_cast<SW1*>(d);
    pc1->I = 2;
    static_cast<Other*>(pc1)->I = 3;
    static_cast<HW*>(pc1)->I = 4;
    std::cerr << d->I;
    std::cerr << static_cast<Other*>(d)->I;
    std::cerr << static_cast<HW*>(d)->I;
}

Отпечатки:

234

Т.е. один и тот же объект d содержит 3 разные версии I, в зависимости от того, как вы смотрите.

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