Derived1 :: Base и Derived2 :: Base относятся к одному и тому же типу? - PullRequest
12 голосов
/ 28 апреля 2020

MSV C, Clang и G CC не согласны с этим кодом:

struct Base { int x; };
struct Der1 : public  Base {};
struct Der2 : public  Base {};

struct AllDer : public Der1, public Der2 {
    void foo() {
        Der1::Base::x = 5;
    }
};

Godbolt

G CC:

<source>: In member function 'void AllDer::foo()':    
<source>:10:21: error: 'Base' is an ambiguous base of 'AllDer'    
   10 |         Der1::Base::x = 5;    
      |                     ^    
Compiler returned: 1

Clang выдает аналогичную ошибку, а MSV C не выдает ошибки.

Кто здесь?

Полагаю, это описано в [class.member .lookup] , но мне трудно понять, что он пытается мне сказать по этому делу. Пожалуйста, процитируйте соответствующие части и, если возможно, объясните на простом английском языке sh.

PS: Вдохновлен этим вопросом Почему ссылка на базовый класс неоднозначна с классом, полученным через :: -оператор?

PPS: На самом деле я сомневаюсь, относится ли Der1::Base к типу, который будет Base (а затем Der2::Base точно такого же типа), или к подобъекту. Я убежден, что это первое, но если оно второе, то MSV C будет прав.

Ответы [ 2 ]

6 голосов
/ 28 апреля 2020

Чтобы ответить на вопрос в заголовке, да, Derived1::Base ссылается на имя введенного класса [class.pre] Base, как и Derived2::Base. Оба относятся к классу ::Base.

Теперь, если Base будет иметь stati c member x, то поиск Base::x будет однозначным. Есть только один.

Проблема в этом примере заключается в том, что x не является членом c, и AllDer имеет двух таких членов. Вы можете устранить неоднозначность такого доступа к x, указав однозначный базовый класс AllDer, который имеет только один член x. Derived1 является однозначным базовым классом, и в нем есть один x член, поэтому Derived1::x однозначно указывает, какой из двух x членов в AllDer вы имеете в виду. У Base тоже есть только один x член, но это не однозначное основание AllDer. Каждый экземпляр AllDer имеет два подобъекта типа Base. Поэтому Base::x неоднозначно в вашем примере. А поскольку Derived1::Base - это просто другое имя для Base, это остается неоднозначным.

[class.member.lookup] указывает, что x ищется в контексте спецификатора вложенного имени, так что это должно быть решено в первую очередь. Мы действительно ищем Base::x, а не Derived1::x, потому что мы начали с разрешения Derived1::Base как Base. Эта часть выполнена успешно, только одна x в Base. Note 12 в [class.member.lookup] явно говорит вам, что использование однозначного поиска имени может все же завершиться неудачей, когда есть несколько подобъектов с тем же именем. D::i в этом примере в основном ваш Base::x.

2 голосов
/ 28 апреля 2020

Причина, по которой вы можете ссылаться на имя класса как члена класса, заключается в том, что cpp использует его псевдоним для удобного использования, как если бы вы написали using Base = ::Base; внутри Base.
Проблема, с которой вы сталкиваетесь, заключается в том, что Der1::Base равно Base.
Таким образом, когда вы пишете Der1::Base::x, оно совпадает с Base::x.

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