3つの主要なC ++コンパイラでコンパイルが異なるプログラム。どちらが正しいですか?


116

前の質問の興味深いフォローアップとして(ただし、実用的にはそれほど重要ではありません)、 C ++では、変数を宣言するときに変数名を括弧で囲むことができるのはなぜですか。

括弧内の宣言と注入されたクラス名機能を組み合わせると、コンパイラの動作に関して驚くべき結果になる可能性があることがわかりました。

次のプログラムを見てください。

#include <iostream>
struct B
{
};

struct C
{
  C (){ std::cout << "C" << '\n'; }
  C (B *) { std::cout << "C (B *)" << '\n';}
};

B *y = nullptr;
int main()
{
  C::C (y);
}
  1. g ++ 4.9.2でコンパイルすると、次のコンパイルエラーが発生します。

    main.cpp:16:10: error: cannot call constructor 'C::C' directly [-fpermissive]
  2. MSVC2013 / 2015で正常にコンパイルされ、印刷されます C (B *)

  3. clang 3.5で正常にコンパイルされ、出力されます C

だから、義務的な質問はどちらが正しいですか?:)

(私は強くclangバージョンに向かって動いており、技術的にtypedefを変更した後に変数の宣言を停止するmsvcの方法はtypedefがちょっと奇妙に見えます)


3
C::C y;意味がありませんよね?どちらもないC::C (y); 最初に私にはこれが最も難問-解析のインスタンスだと思ったんstackoverflow.com/questions/tagged/most-vexing-parseが、今私はそれがすべての3つのコンパイラであるという意味だけで未定義の動作だと思う「右」。
デールウィルソン

4
#3 clangは間違いなく間違っています、#2 msvcは許容度が高すぎ、#1 g ++は正しいです(私は推測します)

8
C::Cタイプを指定するのではなく、関数を指定するので、GCCはまさにimoです。
Galik


回答:


91

GCCは正しく、少なくともC ++ 11ルックアップルールに従っています。3.4.3.1 [class.qual] / 2は、ネストされた名前指定子がクラス名と同じである場合、注入されたクラス名ではなくコンストラクターを参照することを指定します。例を示します:

B::A ba;           // object of type A
A::A a;            // error, A::A is not a type name
struct A::A a2;    // object of type A

それは一時的な作成関数スタイルのキャスト式としてMSVCの誤解それのように見えるCyコンストラクタのパラメータとしては、そしてClangはそれyをtype と呼ばれる変数の宣言と誤って解釈しますC


2
はい、3.4.3.1 / 2が重要です。よくやった!
オービットの軽量レース、

「関数名が無視されない検索で」と書かれています。与えられた例では、特にA::A a;、関数名は無視されるべきだと私には思われます-そうでないのですか?
コロンボ2015

1
N4296の番号付けによると、キーは実際には3.4.3.1/2.1です。「ネストされた名前指定子の後に指定された名前がCで検索された場合、Cの注入されたクラス名である場合...代わりに、名前はクラスCのコンストラクターの名前と見なされます。」Mikeの要約は少し単純化されていますが、たとえば、クラス内のクラス名のtypedefを使用すると、クラス名とは異なるネストされた名前指定子がクラス名を引き続き参照できるため、クラス名を参照できます。俳優。
Jerry Coffin

2
@Mgetz:質問から:「MSVC2013 / 2015で正常にコンパイルされ、出力されますC (B *)
オービットのライトネスレース2015年

2
完全を期すために、これはそれが診断を必要とする不正な形式であるか、または診断を必要としない不正な形式であるかを明確にする必要があります。後者の場合、すべてのコンパイラは「適切」です。
MM

16

エラーが発生するため、G ++は正しいです。なぜなら、コンストラクターは、new演算子なしでこのような形式で直接呼び出すことができないからです。また、コードはを呼び出しC::Cますが、コンストラクタの呼び出しのように見えます。ただし、C ++ 11標準3.4.3.1によると、これは正当な関数呼び出しまたは型名ではありません(Mike Seymourの回答を参照)。

Clangは、正しい関数を呼び出さなくても間違っています。

MSVCは妥当なものですが、それでも標準に準拠していません。


2
newオペレーターは何を変更しますか?
Neil Kirk

1
@NeilKirk:new B(1,2,3)一時的なインスタンス化B(1,2,3)や宣言とは異なり、それがなんらかの「直接コンストラクター呼び出し」(もちろん、そうではない)であると考える人にとっては、たくさんありますB b(1,2,3)
オービットの軽量レース、2015

@LightningRacisinObrit何new B(1,2,3)と言いますか?
user2030677

1
@ user2030677:キーワードnew、型名、およびコンストラクター引数リストを使用したnew-expression 。それでも「直接コンストラクター呼び出し」ではありません。
オービットのライトネスレース'16年

「Clangは正しい関数を呼び出さないので間違っています。」:(宣言内のかっこについてのOPのコメントのため)Clangはと解釈C::C (y); するC::C y;、つまりC型の変数yの定義(注入されたC型を使用)だと思います。C :: Cをコンストラクタにする、ますますめちゃくちゃになる言語仕様の3.4.1,2を誤って無視しながら:C。あなたが思うように、それはかなり明白なエラーではありません、imo。
ピーター-モニカの復活2015年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.