7

以下は、純粋に学術的に発明されたクラス階層です。

struct X{
        void f1();
        void f2();
        void f3();
};

struct Y : private X{
        void f4();
};

struct Z : X{
};

struct D : Y, Z{
        using X::f2;
        using Z::X::f3;
};

int main(){}

「X」は「D」のあいまいなベースであるため、X::f2 の使用宣言はあいまいであると予想していました (X の可視性とアクセシビリティ)。ただし、g++ (ideone.com) では問題なくコンパイルされます。

Online Comeau で確認したところ、期待どおりに X::f2 の宣言を使用するとエラーが発生します。ただし、Z::X::f3 の宣言を使用する場合にもあいまいさが生じます。

では、期待される動作は何ですか?

編集1:

基準の適切なセクションへの参照が役に立ちます。

編集2:

VS 2010 で確認したところ、using 宣言 X::f2 のみに異論があります。ただし、「X」のあいまいさについてではありません (gcc と Comeau の場合のように)。「エラー C2876: 'X': すべてのオーバーロードにアクセスできるわけではありません」についてです。

編集3:

struct X{
    void f(){}
};

struct Y : X{
    struct trouble{
        void f(){}
    };

};

struct trouble : X{
};

struct letscheck : Y, trouble{
    using trouble::f;
};

int main(){}

ここでは、(意図的に) 宣言を使用する際の型に関する問題を作成しようとしました。Gcc はまだこれをうまくコンパイルしており、VS2010 も同様です。Comeau は、あいまいな型の「トラブル」について、(予想どおり) エラーを返します。最初のクエリの説明を見ると、GCC と VS2010 が間違っているようです。あれは正しいですか?

4

3 に答える 3

2

私は、これらのいずれもが不適切であるとは思わない。まず、 forusing X::f2X検索され、これにより明確にクラス type が生成されXます。次にf2inXが検索され、これも明確です ( in D! では検索されません)。

2 番目のケースも同じ理由で機能します。

ただし、オブジェクトを呼び出す 場合、名前はtypeのすべてのサブオブジェクトで検索され、そのようなサブオブジェクトが 2 つあり、非静的メンバー関数であるため、呼び出しがあいまいになります。同じ理由が 2 番目のケースにも当てはまります。直接または使用して名前を付けるかどうかは、これに違いはありません。これらはどちらもクラスを指定します。f2Df2DXDf2f3Z::XXX

using 宣言のあいまいさを得るには、別の方法で記述する必要があります。C++0xusing ThisClass::...;では無効であることに注意してください。ただし、名前全体が基本クラスのメンバーを参照している限り、これは C++03 にあります。

逆に、これが C++0x で許可される場合は、using 宣言全体も有効になります。これは、C++0x が名前検索でサブオブジェクトを考慮しないためです。D::f2明確に 1 つの宣言( の 1 つX) のみを参照します。DR #39と最終論文N1626を参照してください。

struct D : Y, Z{
    // ambiguous: f2 is declared in X, and X is a an ambiguous base class
    using D::f2;
 
    // still fine (if not referred to by calls/etc) :)
    using Z::X::f3;
};

struct E : D {
  // ambiguous in C++03
  // fine in C++0x (if not referred to by an object-context (such as a call)).
  using D::f2;
};

C++03 標準では、段落10.2およびでこれについて説明してい3.4.3.1ます。


Edit3 の応答:

はい、GCC と VS2010 は間違っています。troubleは、 の注入されたクラス名によって検出された型と、::troubleとして検出されたネストされたクラスを参照しY::troubleます。の前の名前は、オブジェクト、関数、および列挙子の名前を無視して (最初の箇条書きでにtrouble委譲する::によって) 非修飾ルックアップを使用して検索されます (ただし、この場合、そのような名前はありません)。次に、次の要件に違反します。3.4.1/710.23.4.3/110.2

結果として得られる宣言のセットがすべて同じ型のサブオブジェクトからのものではない場合...プログラムの形式が正しくありません。


VS2010 と GCC が C++0x の文言をコモーとは異なる方法で解釈し、その文言をさかのぼって実装する可能性があります。

メンバー宣言として使用される using 宣言では、nested-name-specifier は、定義されているクラスの基本クラスに名前を付けるものとします。

これは、非基底クラス考慮されることを意味しますが、非基底クラスが指定されている場合はエラーになります。標準が非基本クラス名を無視するつもりなら、can only here と言うか、明示的に綴ります (両方のプラクティスが行われます)。ただし、この標準は、 shouldおよびcanの使用とはまったく関係ありません。また、GCC は C++0x の文言を実装しています。これは、using 宣言にそのクラス名が含まれているという理由だけで、それ以外の場合は完全に問題のない C++03 コードを拒否するためです。

不明確な表現の例として、次の表現を考えてみましょう。

a.~A();

がクラス オブジェクトの場合はメンバー関数呼び出しになる可能性がありますが、スカラー型 ( などa) の場合は疑似デストラクタ呼び出し (ノーオペレーション) になる可能性があるため、これは構文的にあいまいです。しかし、標準が述べているのは、疑似デストラクタ呼び出しの構文と、それぞれ および でのクラス メンバー アクセスに関するものです。aint5.2.45.2.5

ドット演算子の左辺はスカラー型でなければなりません。

最初のオプション (ドット) の場合、最初の式 (オブジェクト式) の型は「クラス オブジェクト」(完全な型) になります。

あいまいさがまったく解消されないため、これは間違った使い方です。「can only」を使用する必要があり、コンパイラはそのように解釈します。一部の委員会メンバーが最近usenetで私に言ったように、これには主に歴史的な理由があります。国際規格の構造と起草に関する規則、附属書 Hを参照してください。

于 2010-08-23T10:14:26.010 に答える
0

まず、ヨハネスの答えを明確にします。あなたが言うときusing Z::X::f2;、コンパイラはf2アクセス方法を追跡するために「パスを構築」しません。Z::Xは と同じことなので、X宣言は と言っているのとまったく同じusing X::f2;です。次の例と比較してください。

struct A { void f() {} void g() {} };
struct B { void f() {} void g() {} };
struct C { typedef A X; };
struct D { typedef B X; };
struct E : A, B {
    using C::X::f; // C::X == A
    using D::X::g; // D::X == B
};

この構文Z::Xが機能するのは、継承やメンバーシップのためではなくX、スコープから識別子にアクセスできるためZです。Z::Z::Z::Z::X::X::X::Xすべてのクラスが独自の名前を独自のスコープに持ち込むため、うんざりして書くことさえ許されています。したがって、::ここでは継承を表現しません。

さて、問題を解決します。とからf2継承されます。したがって、これはおよびのファーストクラスのメンバーです。これは実装の詳細が隠されているため、知る必要はありません。あなたが望んでいるのはYZXYZEX

struct D : Y, Z{
    using Y::f2; // error: inaccessible
    using Z::f3;
};

あなたが尋ねるように9.1 / 2の観点から説明するには:

クラス名は、クラス名が表示された直後に宣言されているスコープに挿入されます。class-name もクラス自体のスコープに挿入されます。これは、注入されたクラス名として知られています。

名前Xは as に注入さXX::Xます。Yその後、およびに継承されZます。独自のスコープで暗黙的に宣言しないYでください。ZX

10.2/2:

次の手順では、クラス スコープ C での名前検索の結果を定義します。最初に、クラスとその基本クラスのサブオブジェクトのそれぞれでの名前のすべての宣言が考慮されます。…宣言の結果セットがすべて同じ型のサブオブジェクトからのものではない場合、またはセットに非静的メンバーがあり、別個のサブオブジェクトからのメンバーが含まれている場合、あいまいさがあり、プログラムの形式が正しくありません。それ以外の場合、そのセットはルックアップの結果です。

サブオブジェクトの複数形を太字にしたことに注意してください。名前Xは 2 つのサブオブジェクトにありますが、どちらも同じタイプ、つまりXです。

于 2010-08-23T10:41:02.840 に答える
0

X::f2 を使用します。以下のコードの非公開継承のため、機能しないはずです

struct Y : private X{
    void f4();
};

Y を介して X のメンバーにアクセスすることはできません。したがって、X::f2 は競合します。

Z::X::f2動作するはずです。またはZ::f2動作するはずです。

于 2010-08-23T09:53:45.933 に答える