为什么两个 using 子句解析为同一类型在 gcc 中被视为不明确

Why are two using clauses resolving to the same type seen as ambigious in gcc

我有两个带有 using 子句的基础 classes

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


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

然后我声明一个class

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

编译器然后标记对 'NetworkPacket' 的错误引用不明确 'sendNetworkPacket(NetworkPacket &... '

现在 'using clauses' 都解析为相同的基础 class Networking:NetworkPacket

事实上,如果我将方法声明替换为:

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

编译正常。

为什么编译器将每个 using 子句视为不同的类型,即使它们都指向相同的基础类型。这是标准强制要求的还是我们有编译器错误?

来自docs

A type alias declaration introduces a name which can be used as a synonym for the type denoted by type-id. It does not introduce a new type and it cannot change the meaning of an existing type name.

尽管这两个 using 子句表示相同的类型,编译器在以下情况下有两种选择:

void sendNetworkPacket(const NetworkPacket &pdu);

可以选择:

  • MultiCmdQueueCallback::NetworkPacket
  • PlcMsgFactoryImplCallback::NetworkPacket

因为它继承自 MultiCmdQueueCallbackPlcMsgFactoryImplCallback 基础 类。编译器名称解析的结果是您遇到的歧义错误。为了解决这个问题,您需要明确指示编译器使用这样的一个或另一个:

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

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

您只需手动选择要使用的一个即可解决歧义。

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

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

}

编译器只查找基类中的定义。如果相同的类型和/或别名存在于两个基 类 中,它只是抱怨它不知道使用哪一个。结果类型是否相同并不重要。

编译器只在第一步中查找名称,如果这个名称是函数、类型、别名、方法或其他任何名称,则完全独立。如果名称不明确,编译器将不会采取进一步的行动!它只是抱怨错误消息并停止。因此,只需使用给定的 using 语句解决歧义。

在查看别名结果类型之前,(和可访问性)

我们看名字

事实上,

NetworkPacket 可能是

  • MultiCmdQueueCallback::NetworkPacket
  • PlcMsgFactoryImplCallback::NetworkPacket

它们都指向 Networking::NetworkPacket 的事实无关紧要。

我们进行名字解析,这会导致歧义。

有两个错误:

  1. 访问私有类型别名
  2. 对类型别名的引用不明确

私有-私有

我没有看到编译器首先抱怨第二个问题的问题 因为顺序并不重要 - 您必须解决这两个问题才能继续。

public-public

如果您同时更改 MultiCmdQueueCallback::NetworkPacketPlcMsgFactoryImplCallback::NetworkPacket 到 public 或 protected,那么第二个问题(歧义)很明显 - 它们是两个不同的类型别名,尽管它们具有相同的基础数据类型。有些人可能认为 "clever" 编译器可以为您解决这个(特定案例),但请记住,编译器需要 "think in general" 并根据全局规则做出决定,而不是做出特定案例的例外.想象一下以下情况:

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


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

编译器是否应该对 NetworkPacketID 两者一视同仁?一定不。因为在 32 位系统上,size_t 是 32 位长而 uint64_t 总是 64 位。但是,如果我们希望编译器检查底层数据类型,那么它无法区分 64 位系统上的数据类型。

public-私有

我相信这个例子在 OP 的用例中没有任何意义,但是因为在这里 我们是在解决一般问题,让我们考虑一下:

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

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

我认为在这种情况下,编译器应该将 PlcNetwork::NetworkPacket 视为 PlcMsgFactoryImplCallback::NetworkPacket,因为它没有其他选择。为什么它仍然拒绝这样做并归咎于歧义对我来说是一个谜。