10

次のようなコードに出くわしました:

struct A {
    A() {}
    A(int) {}
};

struct B : A {
    void init(int i);
};

void B::init(int i) {
    A::A(i); // what is this?
}

int main() {
    B b;
    b.init(2);
}

これは、VC11 ベータ版を使用してコンパイルおよび実行され、/W4 によるエラーや警告は発生しませんでした。

明らかな意図は、B::init を呼び出して、B の A ベース サブオブジェクトを再初期化することです。itypeという名前の新しい変数の変数宣言として実際に解析されると思いますA。clang でコンパイルすると、診断が生成されます。

ConsoleApplication1.cpp:11:14: warning: declaration shadows a local variable
        A::A(i);
             ^
ConsoleApplication1.cpp:10:22: note: previous declaration is here
    void B::init(int i) {
                     ^
ConsoleApplication1.cpp:11:14: error: redefinition of 'i' with a different type
        A::A(i);
             ^
ConsoleApplication1.cpp:10:22: note: previous definition is here
    void B::init(int i) {
                     ^

冗長なクラス修飾で型を参照できるのは不思議です。

また、A::A(i)VS11 と clang/gcc では異なる方法で解析されるようです。A::A(b)clang と gccを実行すると、デフォルトのコンストラクターを使用してb型の変数が作成されます。VS11は、不明な識別子Aであると言ってエラーを出します。bVS11は、コンストラクターをパラメーターとして使用しA::A(i)て一時的な作成として解析するように見えます。冗長な修飾子が削除されると、VS はソースを clang や gcc のように変数宣言として解析し、変数のシャドウイングに関する同様のエラーを生成します。AA::A(int)ii

この解析の違いにより、VS11 が複数の追加の修飾子で停止する理由が説明されます。A::A::A::A(i)、そしてその理由は、clang と gcc が 1 つの余分な修飾子を受け入れることができることを考えると、2 つ以上の余分な数は 1 つの余分なものと同じ結果になります。

別のコンテキストで冗長な修飾子を使用した別の例を次に示します。すべてのコンパイラは、これを一時的な構造として解析するようです:

class Foo {};

void bar(Foo const &) {}

int main() {
    bar(Foo::Foo());
}
  1. 冗長な修飾子が許可されるのはなぜですか?
  2. コンストラクターを継承するための構文 ( ) など、コンストラクターを参照できるコンテキストがいくつかありますがclass D : B { using B::B; };、VS はどこでもそれを許可しているようです。冗長な修飾子がどのように解析されるかという点で、VS は間違っていて、clang と gcc は正しいですか?
  3. 標準への準拠に関しては、VSがまだかなり遅れていることは知っていますが、最近の活発に開発されたコンパイラが非常に多様であることに少し驚かされます。この場合、冗長な修飾子をコンストラクタの名前として解決します(コンストラクターには名前がありません) と、冗長な修飾子を単純に型に解決することで、VS は一時的に他の修飾子が変数を宣言する場所を構築します。は、clang と gcc によって解析される場所が最も厄介な解析としてさらに悪化する可能性がありますが、VS は、初期化子を使用して型B b(A::A(i));の変数を宣言していると見なします。これほど深刻な違いがまだたくさんありますか?bB
  4. 明らかに、移植可能なコードでは冗長な修飾子を避ける必要があります。この構造が使用されないようにする良い方法はありますか?
4

2 に答える 2

7

この現象はおそらくクラス名の注入に起因する可能性がありますが、ephemient's answer に記載されているように、この特定の例では、かなり前に C++ 言語によって禁止されています。

http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#147

この組み合わせA::Aは、クラスに挿入された名前ではなく、クラス コンストラクターを参照するために必要です。はA::A(i)、準拠するコンパイラによって、コンストラクタ名を含む不正な (したがって意味のない) 式として解釈されることになっています。一例として、Comeau コンパイラは、その理由でコードのコンパイルを拒否します。

どうやら VC11 は引き続きA::A注入されたクラス名への参照として扱います。興味深いことに、VS2005 ではこの問題は見られません。

A::Aが注入された名前を参照していると解釈されていた時代には、オブジェクトを次のように宣言できましAた。

A::A::A::A::A::A a;

など、任意の数のAs を使用します。もうそうじゃない。驚いたことに、ideone で使用されている GCC のバージョン (4.3.4?) には、まだこの問題が残っています。

http://ideone.com/OkR0F

お使いのバージョンの VC11 でこれを試して、それが可能かどうかを確認してください。

于 2012-07-11T00:41:40.617 に答える