Почему два предложения с использованием одного и того же типа рассматриваются как неоднозначные в gcc - PullRequest
32 голосов
/ 12 февраля 2020

У меня есть два базовых класса с использованием предложений

 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);

он прекрасно компилируется.

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

Ответы [ 4 ]

28 голосов
/ 12 февраля 2020

Перед рассмотрением псевдонима результирующего типа (и доступности)

мы рассмотрим имен

, и действительно,

NetworkPacket может быть

  • MultiCmdQueueCallback::NetworkPacket
  • или PlcMsgFactoryImplCallback::NetworkPacket

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

Мы сделать разрешение имени, что приводит к неоднозначности.

14 голосов
/ 12 февраля 2020

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

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

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

}

Компилятор ищет только определения в базовых классах. Если в обоих базовых классах присутствует один и тот же тип и / или псевдоним, он просто жалуется, что не знает, какой из них использовать. Не имеет значения, является ли результирующий тип одинаковым или нет.

Компилятор ищет имена только на первом шаге, полностью независимый, если это имя является функцией, типом, псевдонимом, методом или чем-то еще. Если имена неоднозначны, компилятор не предпринимает никаких дальнейших действий! Он просто жалуется с сообщением об ошибке и останавливается. Так что просто разрешите неоднозначность с помощью данного оператора using.

8 голосов
/ 12 февраля 2020

Из документов :

Объявление псевдонима типа вводит имя, которое можно использовать в качестве синонима для типа, обозначаемого идентификатором типа. Он не вводит новый тип и не может изменить значение имени существующего типа.

Хотя эти два предложения using представляют один и тот же тип, у компилятора есть два варианта в следующей ситуации:

void sendNetworkPacket(const NetworkPacket &pdu);

Можно выбирать между:

  • MultiCmdQueueCallback::NetworkPacket и
  • PlcMsgFactoryImplCallback::NetworkPacket

, поскольку он наследуется от обоих MultiCmdQueueCallback и PlcMsgFactoryImplCallback базовые классы. Результатом разрешения имени компилятора является ошибка неоднозначности, которая у вас есть. Чтобы это исправить, вам нужно явно указать компилятору использовать тот или иной код:

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

или

void sendNetworkPacket(const PlcMsgFactoryImplCallback::NetworkPacket &pdu);
2 голосов
/ 12 февраля 2020

Есть две ошибки:

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

private-private

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

publi c -publi c

Если вы измените видимость MultiCmdQueueCallback::NetworkPacket и PlcMsgFactoryImplCallback::NetworkPacket на publi c или защищенный, тогда вторая проблема (неоднозначность) очевидна - это два псевдонима разных типов, хотя они имеют одинаковые базовые данные тип. Некоторые могут подумать, что «умный» компилятор может решить эту проблему (конкретный случай), но имейте в виду, что компилятор должен «мыслить в общем» и принимать решения, основанные на глобальных правилах, вместо того, чтобы указывать случай c исключения. Представьте себе следующий случай:

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


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

Должен ли компилятор обрабатывать оба NetworkPacketID одинаково? Точно нет. Потому что в 32-битной системе size_t имеет длину 32 бита, а uint64_t всегда 64-битную. Но если мы хотим, чтобы компилятор проверял базовые типы данных, он не мог бы различить guish в 64-битной системе.

publi c -private

Я считаю, что этот пример не имеет смысла в сценарии использования OP, но, поскольку здесь мы решаем проблемы в целом, давайте рассмотрим следующее:

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

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

Я думаю, что в этом случае компилятор должен обработать PlcNetwork::NetworkPacket как PlcMsgFactoryImplCallback::NetworkPacket, потому что у него нет другого выбора. Почему он до сих пор отказывается это сделать и обвиняет в неопределенности, для меня загадка.

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