web-dev-qa-db-ja.com

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

私の前の質問への興味深いフォローアップとして(しかし実際的にはそれほど重要ではありません): なぜ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を出力します

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

(ただし、技術的にはtypedefを使用して型を変更しただけで変数の宣言を停止するmsvcの方法は、clangバージョンに強く影響しました)

116
Predelnik

少なくともC++ 11ルックアップルールに従って、GCCは正しいです。 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は、コンストラクターパラメーターとしてCを持つ一時的なyを作成する関数スタイルのキャスト式と誤解しているように見えます。そして、Clangはy型のCという変数の宣言と誤解します。

91
Mike Seymour

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

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

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

16
Kun Ling