Глобальная инициализация с временным объектом функции

следующий код

#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?

2 ответов


gcc анализирует подвыражение (std::random_device())() как приведение к типу функции 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:

cast-expression:

  • унарное-выражение
  • ( type-id ) cast-expression

так как пустая пара скобок () - это не унарное-выражение, это производство недоступно, и компилятор должен выбрать производство из 5.2p1:

постфиксное-выражение:

  • [...]
  • постфиксное-выражение ( список выраженийopt )
  • [...]

здесь постфиксное-выражение is (std::random_device()) и список выражений опущен.

Я подал http://gcc.gnu.org/bugzilla/show_bug.cgi?id=56239 на GCC bugzilla, и похоже, что это должно быть решено в ближайшее время.

обратите внимание, что если вы вводите аргументы 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);

кажется, что есть проблема с тем, как 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 и вызовите своего оператора вызова. Это не может быть объявление функции, потому что if A интерпретируется как тип возвращаемого значения, имя отсутствует (и наоборот).

С другой стороны, (A()) является допустимым выражением для создания временного типа A, и это временное поддерживает оператор вызова (тип возврата которого совпадает с типом, принятым B'конструктор ы). Таким образом, (A())() должно быть допустимым выражением типа bool.

по этим причинам я считаю, что разбор GCC ошибочен.