11

次のコード

#include <random>
std::mt19937 generator((std::random_device())());

ファイルだけをclangでコンパイルします:

$ clang++ -c -std=c++0x test.cpp

しかし、gcc で失敗します:

$ g++ -c -std=c++0x test.cpp 
test.cpp:3:47: erro: expected primary-expression before ‘)’ token

そのコードは C++11 で有効ですか? GCC のバグですか、それとも Clang の拡張機能/バグですか?

4

2 に答える 2

6

gcc は部分式(std::random_device())()を関数 type へのキャストとして解析していますstd::random_device()。icc のエラー出力を見ると、gcc のエラー出力よりも少し有益です。

source.cpp(6): error: cast to type "std::random_device ()" is not allowed
  std::mt19937 generator((std::random_device())());
                          ^

source.cpp(6): error: expected an expression
  std::mt19937 generator((std::random_device())());
                                                ^

関連するプロダクションは 5.4p2 です。

キャスト式:

  • 単項式
  • ( type-id )キャスト式

括弧の空のペアはunary-expression()ではないため、このプロダクションは使用できず、コンパイラは 5.2p1 からプロダクションを選択する必要があります。

接尾式:

  • [...]
  • 後置式(式リストのオプション)
  • [...]

ここで、postfix-expression(std::random_device())あり、expression-listは省略されています。

gcc bugzilla にhttp://gcc.gnu.org/bugzilla/show_bug.cgi?id=56239を提出しましたが、まもなく解決されるはずです。

に引数を指定するoperator()場合、8.2p2 では、関数型へのキャストは違法ですが、コンパイラは式をキャストとして解析する必要があることに注意してください (複数の引数がある場合、引数リストはとして解析されます)。コンマ演算子を使用した式:

(std::less<int>())(1, 2);
^~~~~~~~~~~~~~~~~~ illegal C-style cast
 ^~~~~~~~~~~~~~~~ type-id of function type std::less<int>()
                  ^~~~~~ argument of C-style cast
                    ^ comma operator

これを記述する正しい方法 (C++11 ユニバーサル初期化構文を使用する以外) は、別の層の括弧を追加することです。これは、type-idに外側の括弧を含めることができないためです。

((std::less<int>()))(1, 2);
于 2013-02-06T17:55:12.593 に答える
2

GCC が関数宣言を処理する方法に問題があるようです。次の例を見てください。

struct A
{
    bool operator () () { return true; }
};

struct B
{
    B(bool) { }
};

B b((         // This cannot be parsed as a function declaration,
    A()()     // and yet GCC 4.7.2 interprets it as such:
    ));       // "error: 'type name' declared as function returning 
              // a function B b((A()()));"

int main() { }

の周りに追加の括弧が存在するためA()()、構文形式B b(( A()() ));を関数の宣言として解析できません。

質問の例の宣言は少し異なります。

B b(
   (A())()
   );

ただし、この場合でも、(A())()を返す関数を返す関数の型として解釈することはできませんA(常にb、名前のないパラメーターを持つ関数宣言と見なそうとします)。問題は、他の何かとして解釈できるかどうかです。その場合、およびこのコンテキストで意味がある場合、コンパイラは式全体を object の構築として解析することを検討する必要がありますb

これはおそらく、GCC と Clang が意見を異にする基本的な点です。

int main()
{
    (A())(); // OK for Clang, ERROR for GCC
}

A上記が一時的な型を構築し、その呼び出し演算子を呼び出す試み以外のものとしてどのように解釈されるかわかりません。Aが戻り値の型として解釈される場合、名前が欠落しているため (逆も同様) 、関数宣言にすることはできません。

一方、(A())はタイプ のテンポラリを作成するための有効な式であり、その一時は call 演算子をサポートします (戻り値のタイプはのコンストラクタAによって受け入れられるタイプと同じです)。Bしたがって、(A())()は type の有効な式である必要がありますbool

このような理由から、GCC の構文解析は間違っていると思います。

于 2013-02-06T15:16:17.620 に答える