次のようなコードに出くわしました:
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 ベース サブオブジェクトを再初期化することです。i
typeという名前の新しい変数の変数宣言として実際に解析されると思います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
であると言ってエラーを出します。b
VS11は、コンストラクターをパラメーターとして使用しA::A(i)
て一時的な作成として解析するように見えます。冗長な修飾子が削除されると、VS はソースを clang や gcc のように変数宣言として解析し、変数のシャドウイングに関する同様のエラーを生成します。A
A::A(int)
i
i
この解析の違いにより、VS11 が複数の追加の修飾子で停止する理由が説明されます。A::A::A::A(i)
、そしてその理由は、clang と gcc が 1 つの余分な修飾子を受け入れることができることを考えると、2 つ以上の余分な数は 1 つの余分なものと同じ結果になります。
別のコンテキストで冗長な修飾子を使用した別の例を次に示します。すべてのコンパイラは、これを一時的な構造として解析するようです:
class Foo {};
void bar(Foo const &) {}
int main() {
bar(Foo::Foo());
}
- 冗長な修飾子が許可されるのはなぜですか?
- コンストラクターを継承するための構文 ( ) など、コンストラクターを参照できるコンテキストがいくつかありますが
class D : B { using B::B; };
、VS はどこでもそれを許可しているようです。冗長な修飾子がどのように解析されるかという点で、VS は間違っていて、clang と gcc は正しいですか? - 標準への準拠に関しては、VSがまだかなり遅れていることは知っていますが、最近の活発に開発されたコンパイラが非常に多様であることに少し驚かされます。この場合、冗長な修飾子をコンストラクタの名前として解決します(コンストラクターには名前がありません) と、冗長な修飾子を単純に型に解決することで、VS は一時的に他の修飾子が変数を宣言する場所を構築します。は、clang と gcc によって解析される場所が最も厄介な解析としてさらに悪化する可能性がありますが、VS は、初期化子を使用して型
B b(A::A(i));
の変数を宣言していると見なします。これほど深刻な違いがまだたくさんありますか?b
B
- 明らかに、移植可能なコードでは冗長な修飾子を避ける必要があります。この構造が使用されないようにする良い方法はありますか?