2つのusing句が同じ型に解決されるのはなぜgccではあいまいと見なされるのですか


32

using句を含む2つの基本クラスがあります

 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&...」というフラグを立てます。

これで、両方の「using句」が同じ基本クラスNetworking:NetworkPacketに解決されます

実際、メソッド宣言を次のように置き換えた場合:

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

それはうまくコンパイルします。

コンパイラーがそれぞれのusing節を両方とも同じ基本型を指しているにもかかわらず、異なる型として扱うのはなぜですか?これは標準によって義務付けられていますか、それともコンパイラのバグがありますか?


コンパイラは十分に巧妙ではないようです
idris

重要なのは、この時点でコンパイラが知っているのは、3つあることですNetworkPacket。MultiCmdQueueCallback内、PlcMsgFactoryImplCallback内、ネットワーク内にあります。どちらを使用するかを指定する必要があります。virtualここでパッティングが役に立たないと思います。
theWiseBro

@idris:その代わりに、あなたは標準が十分に寛容ではないことを意味しました。コンパイラは標準に従うのが正しいです。
Jarod42

@ Jarod42以下では、「type-idで示される型の同義語」と回答しているため、同じtype-idがある場合は、両方を使用できます。スタンダートであろうとコンパイラであろうと、誰かが実際には十分に賢くないようです。
idris

マルチ継承の問題の1つ
eagle275

回答:


28

エイリアスの結果の型(およびアクセシビリティ)を見る前に

私たちは名前を見ます

本当に、

NetworkPacket かもしれない

  • MultiCmdQueueCallback::NetworkPacket
  • または PlcMsgFactoryImplCallback::NetworkPacket

彼らが両方とも指すという事実Networking::NetworkPacketは無関係です。

最初の名前解決を行うため、あいまいになります。


実際、これは部分的にのみ当てはまります。PlcNetworkにusingを追加すると、| NetworkPacket = MultiCmdQueueCallback :: NetworkPacket;を使用します。前のusing句がプライベートであるため、コンパイラエラーが発生します。
Andrew Goedhart

@AndrewGoedhart矛盾ではありません。名前の検索は、最初に自分のクラスで開始されます。コンパイラーがそこで固有の名前をすでに見つけているので、それは満足です。
アコンカグア

ここでの私の問題は、名前が基本クラスのプライベート命名句から伝播する理由です。プライベート宣言の1つを削除すると、つまり基本クラスの1つにプライベートのusing句があり、もう1つにはないものがあると、エラーは「ネットワークパケットが型に名前を付けていません」に変わります
Andrew Goedhart

1
@AndrewGoedhart名前の検索(明らかに)ではアクセシビリティは考慮されません。一方をパブリックにし、もう一方をプライベートにすると、同じエラーが発生します。これが最初に発見されるエラーなので、これが最初に出力されるエラーです。エイリアスを1つ削除すると、あいまいさの問題はなくなりますが、アクセス不能の問題が残るため、次のエラーが出力されます。ちなみに、適切なエラーメッセージ(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'); }同じではないが、オーバーロードの解決は同じように機能します。適切な関数を選択した後でのみ、利用可能なすべての関数を考慮してください...与えられた場合、あいまいさも生じます。2つの文字を受け入れるようにprivat関数を変更すると、プライベートではありますが選択され、次のコンパイルエラーが発生します。
アコンカグア

14

どちらを使用するかを手動で選択することで、あいまいさを解決できます。

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

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

}

コンパイラは、基本クラスの定義のみを探します。同じタイプまたはエイリアスが両方の基本クラスに存在する場合、どちらを使用するかわからないというメッセージが表示されます。結果の型が同じかどうかは関係ありません。

コンパイラは最初のステップで名前を検索するだけで、この名前が関数、型、エイリアス、メソッドなどである場合は完全に独立しています。名前があいまいな場合は、コンパイラからそれ以上のアクションは行われません。エラーメッセージが表示されて停止するだけです。したがって、指定されたusingステートメントで曖昧さを解決するだけです。


言い回しに疑問があります。定義を調べたら、その型も考慮しませんか?名前だけを検索するのではないでしょうか(定義方法は忘れてください)。標準へのいくつかの参照は素晴らしいでしょう...
アコンカグア

この最後のコメントは、その理由を正しく説明するものです。最後の段落をこのコメントで置き換えてください。投票します;)
アコンカグア

受け入れることはできません–私は質問の著者ではありません...あなたの緊張に乗っていたとしたら、申し訳ありません。以前はQAの核となる質問に答えていないと感じたので、答えを改善しようとしています...
アコンカグア

@アコンカグア:Ubs、私の責任:-)改善をありがとう!
クラウス

実際には、両方のusing句がプライベートであるため、これは機能しません。PlcNetworkにusingを追加した場合:NetworkPacket = MultiCmdQueueCallback :: NetworkPacket;を使用します。前のusing句がプライベートであるため、コンパイラエラーが発生します。ちなみに、1つの基本クラスを使用する句をパブリックにして、もう1つをプライベートにしても、あいまいなエラーが発生します。基本クラスで定義されていないメソッドで曖昧なエラーが発生します。
Andrew Goedhart

8

ドキュメントから:

型エイリアス宣言は、type-idで示される型の同義語として使用できる名前を導入します。新しいタイプは導入されず、既存のタイプ名の意味を変更することもできません。

これら2つのusing句は同じ型を表しますが、コンパイラーには次の状況で2つの選択肢があります。

void sendNetworkPacket(const NetworkPacket &pdu);

以下から選択できます。

  • MultiCmdQueueCallback::NetworkPacket そして
  • PlcMsgFactoryImplCallback::NetworkPacket

MultiCmdQueueCallbackPlcMsgFactoryImplCallback基本クラスの両方から継承するためです。コンパイラーの名前解決の結果は、あいまいなエラーになります。これを修正するには、次のようにコンパイラを使用するようにコンパイラに明示的に指示する必要があります。

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

または

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

正直言って、私は満足していません...同じタイプの同義語です。私は簡単にできますclass C { void f(uint32_t); }; void C::f(unsigned int) { }(エイリアスが一致する場合)。なぜここに違いがあるのですか?それらはまだ同じタイプであり、あなたの引用によって確認されました(説明するには不十分だと思います)...
アコンカグア

@Aconcagua:ベースタイプまたはエイリアスを使用しても違いはありません。エイリアスが新しいタイプになることはありません。あなたの観察は、2つの基本クラスにSAMEエイリアスを与えることによって生成する曖昧さとは何の関係もありません。
クラウス

1
@アコンカグアあなたが言及した例は、質問の状況に相当するものではないと思います
NutCracker

さて、少し変更しましょう。クラスA、B、Cとtypedef Dに名前を付けましょうclass C : public A, public B { void f(A::D); }; void C::f(B::D) { }。そうすれば、次のこともできます。– 少なくともGCCは受け入れます。
アコンカグア

質問の作者は文字通り「なぜ両方のコンパイラが同じ基になる型を指しているにもかかわらず、using句を個別の型として扱うのですか?」–そして、引用が理由をどのように明確にするはわかりませんが、QAの混乱を確認するだけです。答えが間違っているとは言いたくありませんが、私の目では十分に明確にされていません。 。
アコンカグア

2

2つのエラーがあります。

  1. プライベートタイプエイリアスへのアクセス
  2. 型エイリアスへのあいまいな参照

プライベート・プライベート

順序は重要ではないため、コンパイラが2番目の問題について最初に文句を言う問題はありません。続行するには両方の問題を修正する必要があります。

官公

両方の可視性をpublicまたはprotectedのいずれかに変更するMultiCmdQueueCallback::NetworkPacketPlcMsgFactoryImplCallback::NetworkPacket、2番目の問題(あいまいさ)は明らかです。これらは、基礎となるデータ型は同じですが、2つの異なる型エイリアスです。「賢い」コンパイラーがこれを解決できる(特定のケース)と考える人もいますが、コンパイラーは、ケース固有の例外を作成するのではなく、「一般的に考えて」、グローバルルールに基づいて決定を行う必要があることに注意してください。次のケースを想像してみてください:

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


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

コンパイラは両方をNetworkPacketID同じように扱うべきですか?わかりません。32ビットシステムでsize_tは、32ビット長uint64_tですが、常に64ビットです。しかし、コンパイラに基になるデータ型をチェックさせたい場合、64ビットシステムではそれらを区別できません。

官民

この例はOPのユースケースでは意味をなさないと思いますが、ここでは一般的に問題を解決しているので、それを考慮しましょう。

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

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

私は、この場合には、コンパイラが扱うべきだと思うPlcNetwork::NetworkPacketようPlcMsgFactoryImplCallback::NetworkPacketには他の選択を書くことがありませんので。それがなぜそうすることをまだ拒否し、曖昧さのせいにするのかは私にとって謎です。


「なぜそうすることを拒否し、曖昧さのせいにするのかは、私には謎です。」C ++では、名前の検索(可視性)がアクセスチェックの前に行われます。IIRC、私は、名前をプライベートからパブリックに変更しても既存のコードが壊れることはないという論理的根拠があることをどこかで読みましたが、完全にはわかりません。
LF
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.