2016-04-21 7 views
12

私はこのコード(ダイヤモンドの問題を)持っている:ダイヤモンド(C++)

#include <iostream> 
using namespace std; 

struct Top 
{ 
    void print() { cout << "Top::print()" << endl; } 
}; 

struct Right : Top 
{ 
    void print() { cout << "Right::print()" << endl; } 
}; 

struct Left : Top 
{ 
    void print() { cout << "Left::print()" << endl; } 
}; 

struct Bottom: Right, Left{}; 

int main() 
{ 
    Bottom b; 
    b.Right::Top::print(); 
} 

私はTopクラスでprint()を呼びたいです。

コンパイルしようとするとエラーが表示されます:'Top' is an ambiguous base of 'Bottom'この行に:b.Right::Top::print(); なぜ曖昧ですか?私は、TopRightから、Leftにではなく明示的に指定しました。

私はそれを行う方法を知りたくありません。はい、参照、仮想継承などで行うことができます。私はちょうどb.Right::Top::print();があいまいであることを知りたいと思います。

+0

これは暗黙の "this->"を含むクラスメンバアクセス演算子が非静的データメンバまたは非静的メンバ関数にアクセスするために使用された場合、参照が不正である左のオペランド( "。オペレータの場合)は、右オペランド "*、11.2p6の命名クラスへのポインタに暗黙的に変換することはできません。命名クラスは' A'ですが、 'D *'は暗黙的に 'A *'に変換できません。 –

+0

ここでのセマンティクスは、 'B :: A :: tell'を使ってどの関数を呼び出すかを教えてくれることです。あなたは' D :: tell'を使ってコンパイラを助けます。サブオブジェクトを指定する必要はありません - それは2つの選択肢があります: 'B 'または' C'を介して 'A'への道を進み、エラーを返します。 –

+0

2つの"主要な "あいまいチェックがあります5.2.5p5にあるものと、11.2p6でここに噛み付くものです。5.2.5p5のものは、すべてのtell関数を削除する場合は 'd.tell()'を拒否します'A'の名前を除いて' D'であるが、 'tell'は' A'の直接のメンバーになり、 'A'はあいまいです。' DB :: A :: tell() '、それは5.2.5p5で整形式ですしかし、11.2p6では不調に終わった。これらのチェックはお互いを補完するものであり、タイプシステムで適切に動作するために重要です。 –

答えて

15

Why is it ambiguous? I explicitly specified that I want Top from Right and not from Left .

これはあなたの意図でしたが、実際には起こりません。 Right::Top::print()は、呼び出すメンバ関数の名前を明示的に指定します(&Top::print)。しかし、それはbのサブオブジェクトに、そのメンバ関数を呼び出すことを指定していません。 printを選択部分が曖昧でない

auto print = &Bottom::Right::Top::print; // ok 
(b.*print)();        // error 

:あなたのコードは、概念的に等価です。 bからTopへの暗黙の変換はあいまいです。あなたは、明示的に同じような何かをすることによって、あなたが行っているどの方向明確にする必要があるだろう:

static_cast<Right&>(b).Top::print(); 
+0

私は 'Right :: Top :: print() '?どちらもポインタへのポインタでは動作しませんが、 'b.Right :: print()'は動作します。 (もちろん 'Right'クラスで' print() 'を削除しました)。 – PcAF

+1

@PcAF 'b'から' Right'への変換が明確であるためです。タイプ「Right」のサブオブジェクトは1つだけです。 – Barry

+0

ありがとうございます。もし 'b''から' 'Right''への変換が曖昧でないならば、なぜ' 'auto print =&Bottom :: Right :: print;' '(b。* print)()'が 'あいまいな' )? – PcAF

4

スコープ解決演算子が左結合される(それは括弧を許可していませんが)。

だから、あなたがBA::tellを参照したいのに対し、ID-表現があいまいである、単にAである、B::Atellを指します。

回避策は、最初に明確なベースBにキャストし、次にAに再度キャストすることです。

言語-lawyering:

[basic.lookup.qual]/1 ネストされた名指定子に関連する文法は、

nested-name-specifier:

    type-name::

    nested-name-specifieridentifier::

ある

The name of a class or namespace member or enumerator can be referred to after the :: scope resolution operator applied to a nested-name-specifier that denotes its class, namespace, or enumeration.

、言いますしたがって、最初のネストされた名前指定子B::であり、Aはそれ。次に、B::Aは、Aを示すネストされた名前指定子であり、tellがその中で参照されます。

明らかに、MSVCはこの例を受け入れます。おそらく、そのような指定子をバックトラックすることによってあいまいさを解決するために、非標準的な拡張があります。

+0

関連する標準語を引用してください。そうでない場合は、これは推測です。 –

+0

@ Cheersandhth.-Alf投稿した後、[言語弁護士]タグが追加されました。私は良い説明をつけることができるかどうかを見ていきますが、右から左への参照は単に不可能です。 – Potatoswatter

+0

@Potatoswatter +1、参照のための私のコメントを参照してください。 –

関連する問題