Чому два використання клаузи, що вирішують один і той же тип, у gcc розглядаються як амбіційні


32

У мене є два базові класи з використанням пропозицій

 class MultiCmdQueueCallback {
  using NetworkPacket  = Networking::NetworkPacket;
  ....
 }


 class PlcMsgFactoryImplCallback {
   using NetworkPacket = Networking::NetworkPacket;
  ....
 }

Потім я оголошую клас

class PlcNetwork : 
  public RouterCallback, 
  public PlcMsgFactoryImplCallback, 
  public MultiCmdQueueCallback {
  private:
    void sendNetworkPacket(const NetworkPacket &pdu);
}

компілятор потім позначає помилку на посилання на "NetworkPacket" неоднозначно "sendNetworkPacket (NetworkPacket & ..."

Тепер обидва "використання пропозицій" вирішуються для одного базового класу Networking: NetworkPacket

і насправді, якщо я заміню декларацію методу на:

 void sendNetworkPacket(const Networking::NetworkPacket &pdu);

вона складається добре.

Чому компілятор розглядає кожне з використанням пункту як окремий тип, хоча вони обидва вказують на один і той же базовий тип. Це передбачено стандартом чи у нас є помилка компілятора?


Здається, компілятор недостатньо розумний
idris

Справа в тому, що цей компілятор просто знає, що їх існує три NetworkPacket- у MultiCmdQueueCallback, у PlcMsgFactoryImplCallback, у Мережі. Який використовувати, слід вказати. І я не думаю, що розміщення virtualтут не допоможе.
theWiseBro

@idris: замість цього ти мав на увазі, що стандарт не є достатньо дозвільним. компілятори мають право слідувати стандарту.
Jarod42

@ Jarod42 Внизу відповідь 'синонім типу, що позначається type-id', тож якщо вони мають однаковий ідентифікатор type, можна дозволити використовувати обидва. будь то стандарт або компілятор, просто здається, що деякі насправді недостатньо розумні.
ідріс

одна з проблем багатонаціонального спадкування
eagle275

Відповіді:


28

Перш ніж переглянути тип псевдоніму (та доступність)

ми дивимося на імена

і справді,

NetworkPacket може бути

  • MultiCmdQueueCallback::NetworkPacket
  • або PlcMsgFactoryImplCallback::NetworkPacket

Те, на що вони обоє вказують, не Networking::NetworkPacketмає значення.

Ми робимо роздільну здатність імені, що призводить до неоднозначності.


Насправді це справедливо лише частково, якщо я додаю використання до PlcNetwork: | використовуючи NetworkPacket = MultiCmdQueueCallback :: NetworkPacket; Я отримую помилку компілятора, оскільки попереднє використання пункту використання є приватним.
Ендрю Гедхарт

@AndrewGoedhart Не протиріччя. Пошук імен починається спочатку у власному класі. Оскільки компілятор потім вже знайде унікальну назву, вона задоволена.
Аконкагуа

Моя проблема в тому, чому ім'я пропонується з приватного пункту іменування в базовому класі. Якщо я видаляю одну з приватних декларацій, тобто один з базових класів має приватне за допомогою пункту, а інший - ні, помилка змінюється на 'Мережевий пакет не називає тип'
Andrew Goedhart

1
Пошук @AndrewGoedhart (очевидно) не враховує доступність. Ви отримуєте однакову помилку, якщо одну публічну, а іншу приватну. Це перша помилка, яку було виявлено, тож це перша помилка, яку слід надрукувати. Якщо ви видалите один псевдонім, проблема двозначності відпадає, але проблема недоступності залишається, тож ви отримуєте друкується наступну помилку. До речі, не дуже гарне сполучення про помилку (? MSVC ще раз), GCC більш точніше про: error: [...] is private within this context.
Аконкагуа

1
@AndrewGoedhart Враховуйте наступне: class A { public: void f(char, int) { } private: void f(int, char) { } }; void demo() { A a; a.f('a', 'd'); }- не те саме, але роздільна здатність перевантаження працює однаково: Розгляньте всі доступні функції, лише після вибору відповідної врахуйте доступність ... У даному випадку ви також отримаєте неоднозначність; якщо ви зміните функцію privat на прийняття двох символів, вона буде вибрана, хоча й приватною - і ви зіткнетеся з наступною помилкою компіляції.
Аконкагуа

14

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

class PlcNetwork : 
  public RouterCallback, 
  public PlcMsgFactoryImplCallback, 
  public MultiCmdQueueCallback {

using NetworkPacket= PlcMsgFactoryImplCallback::NetworkPacket; // <<< add this line
private:
    void sendNetworkPacket(const NetworkPacket &pdu);

}

Компілятор шукає лише визначення в базових класах. Якщо в обох базових класах присутній один і той же тип або псевдонім, він просто скаржиться, що не знає, який з них використовувати. Не має значення, збігається тип чи ні.

Компілятор шукає імена лише на першому кроці, повністю незалежних, якщо це ім'я є функцією, типом, псевдонімом, методом чи іншим способом. Якщо імена неоднозначні, подальша дія з компілятора не робиться! Він просто скаржиться на повідомлення про помилку і зупиняється. Тому просто вирішіть двозначність із заданим оператором.


У вас є деякі сумніви щодо формулювання. Якщо він шукає визначення , чи не врахував би він і тип? Чи не буде він шукати лише імена (і забути про те, як визначено)? Деякі посилання на стандарт були б чудовими ...
Аконкагуа,

Цей останній коментар пояснює, чому саме так . Замініть останній абзац цим коментарем, і я проголошу;)
Аконкагуа,

Я не можу прийняти - я не автор запитання ... Вибачте, якщо я, можливо, у вас на нерви. Просто намагаюся вдосконалити відповідь, оскільки я відчував, що раніше вона не відповіла на основне питання QA ...
Аконкагуа,

@Aconcagua: Ubs, моя вина :-) Дякую за вдосконалення!
Клаус

Насправді це не працює, оскільки обидва, що використовують положення, є приватними. Якщо я додаю використання в PlcNetwork: | використовуючи NetworkPacket = MultiCmdQueueCallback :: NetworkPacket; Я отримую помилку компілятора, оскільки попереднє використання пункту використання є приватним. До речі, якщо я роблю один базовий клас за допомогою пункту public, а інший приватний, я все-таки отримую помилку двозначності. Я отримую помилки неоднозначності щодо методів, які не визначені в базовому класі.
Ендрю Гедхарт

8

З документів :

Декларація псевдоніма типу вводить ім'я, яке може бути використане як синонім типу, позначеного тип-id. Він не вводить новий тип і не може змінити значення існуючого імені типу.

Хоча ці два usingпропозиції представляють один і той же тип, компілятор має два варіанти в наступній ситуації:

void sendNetworkPacket(const NetworkPacket &pdu);

Він може вибрати між:

  • MultiCmdQueueCallback::NetworkPacket і
  • PlcMsgFactoryImplCallback::NetworkPacket

тому що він успадковує MultiCmdQueueCallbackі обидва, і PlcMsgFactoryImplCallbackбазові класи. Результатом вирішення імені компілятора є помилка неоднозначності, яка у вас є. Щоб виправити це, вам потрібно чітко доручити компілятору використовувати те чи інше, як це:

void sendNetworkPacket(const MultiCmdQueueCallback::NetworkPacket &pdu);

або

void sendNetworkPacket(const PlcMsgFactoryImplCallback::NetworkPacket &pdu);

Якщо чесно, я не відчуваю себе задоволеним ... Вони обоє синоніми одного типу. Я легко можу class C { void f(uint32_t); }; void C::f(unsigned int) { }(за умови відповідності псевдоніма). То чому тут різниця? Вони все одно того ж типу, що підтверджується вашою цитатою (яку я не вважаю достатньою для пояснення) ...
Аконкагуа,

@Aconcagua: Використання базового типу або псевдоніма ніколи не має значення. Псевдонім ніколи не є новим типом. Ваше спостереження не має нічого спільного з неоднозначністю, яку ви створюєте, надаючи псевдонім SAME у двох базових класах.
Клаус

1
@Aconcagua Я думаю, що ви, згаданий вами приклад, не є правильним еквівалентом для ситуації з питання
NutCracker

Що ж, давайте трохи змінимось: назвемо класи A, B і C та typedef D, тоді ви навіть можете зробити: class C : public A, public B { void f(A::D); }; void C::f(B::D) { }- принаймні GCC приймає.
Аконкагуа

Автор запитань буквально запитав: "Чому компілятор розглядає кожне використання пункту як окремий тип, хоча вони обоє вказують на один і той же базовий тип?" - і я не бачу, як цитування пояснить, чому , натомість, це просто підтверджує плутанину QA ... Не хочу сказати, що відповідь неправильна , але в моїх очах вона не з'ясовується достатньо. .
Аконкагуа

2

Є дві помилки:

  1. Доступ до псевдонімів приватного типу
  2. Неоднозначне посилання на псевдоніми типів

приватний-приватний

Я не бачу проблеми, що компілятор спочатку скаржиться на другу проблему, оскільки замовлення насправді не має значення - вам потрібно виправити обидві проблеми, щоб продовжити.

громадсько-публічний

Якщо змінити видимість як MultiCmdQueueCallback::NetworkPacketі PlcMsgFactoryImplCallback::NetworkPacketдля будь-якої публіки або захищеною, то друге питання (неоднозначності) очевидно - це два різних псевдоніми типу , хоча вони мають один і той же базовий тип даних. Деякі можуть подумати, що "розумний" компілятор може вирішити це (конкретний випадок) для вас, але майте на увазі, що компілятору потрібно "думати взагалі" та приймати рішення на основі глобальних правил, а не робити винятки, що стосуються конкретних випадків. Уявіть такий випадок:

class MultiCmdQueueCallback {
    using NetworkPacketID  = size_t;
    // ...
};


class PlcMsgFactoryImplCallback {
    using NetworkPacketID = uint64_t;
    // ...
};

Чи повинен компілятор ставитися до обох NetworkPacketIDоднаково? Звичайно, ні. Тому що в 32-бітній системі, вона size_tє 32-бітовою, а uint64_tзавжди 64-розрядна. Але якщо ми хочемо, щоб компілятор перевірив базові типи даних, то він не міг би розрізнити ті, що знаходяться в 64-бітній системі.

державно-приватний

Я вважаю, що цей приклад не має сенсу в застосуванні ОП, але оскільки тут ми вирішуємо проблеми загалом, давайте розглянемо це:

class MultiCmdQueueCallback {
private:
    using NetworkPacket  = Networking::NetworkPacket;
    // ...
};

class PlcMsgFactoryImplCallback {
public:
    using NetworkPacket  = Networking::NetworkPacket;
    // ...
};

Я думаю, що в цьому випадку компілятор повинен ставитися PlcNetwork::NetworkPacketтак, PlcMsgFactoryImplCallback::NetworkPacketяк у нього немає інших виборів. Чому він досі відмовляється це робити, і звинувачує в неоднозначності - для мене загадка.


"Чому він все ще відмовляється це робити, і звинувачує в неоднозначності - для мене загадка". У C ++ пошуку імен (видимості) передує перевірка доступу. IIRC, я десь читав, що обґрунтуванням є те, що зміна імені з приватного на публічне не повинно порушувати існуючий код, але я не зовсім впевнений.
LF
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.