为什么两个 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
因为它继承自 MultiCmdQueueCallback
和 PlcMsgFactoryImplCallback
基础 类。编译器名称解析的结果是您遇到的歧义错误。为了解决这个问题,您需要明确指示编译器使用这样的一个或另一个:
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
的事实无关紧要。
我们进行名字解析,这会导致歧义。
有两个错误:
- 访问私有类型别名
- 对类型别名的引用不明确
私有-私有
我没有看到编译器首先抱怨第二个问题的问题
因为顺序并不重要 - 您必须解决这两个问题才能继续。
public-public
如果您同时更改 MultiCmdQueueCallback::NetworkPacket
和
PlcMsgFactoryImplCallback::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
,因为它没有其他选择。为什么它仍然拒绝这样做并归咎于歧义对我来说是一个谜。
我有两个带有 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
因为它继承自 MultiCmdQueueCallback
和 PlcMsgFactoryImplCallback
基础 类。编译器名称解析的结果是您遇到的歧义错误。为了解决这个问题,您需要明确指示编译器使用这样的一个或另一个:
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
的事实无关紧要。
我们进行名字解析,这会导致歧义。
有两个错误:
- 访问私有类型别名
- 对类型别名的引用不明确
私有-私有
我没有看到编译器首先抱怨第二个问题的问题 因为顺序并不重要 - 您必须解决这两个问题才能继续。
public-public
如果您同时更改 MultiCmdQueueCallback::NetworkPacket
和
PlcMsgFactoryImplCallback::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
,因为它没有其他选择。为什么它仍然拒绝这样做并归咎于歧义对我来说是一个谜。