Подтвердить что ты не робот

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

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

#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

Является ли этот код действительным в С++ 11? Это ошибка в GCC или расширение/ошибка Clang?

4b9b3361

Ответ 1

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:

монолитно-выражение:

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

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

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

  • [...]
  • postfix-expression (выражение-list opt)
  • [...]

где постфиксное выражение (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

Правильный способ записи (кроме использования синтаксиса универсального инициализатора С++ 11) заключается в добавлении другого слоя круглых скобок, поскольку идентификатор типа не может содержать внешние круглые скобки:

((std::less<int>()))(1, 2);

Ответ 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 как объявление функции с неназванным параметром), Итак, вопрос в том, может ли он интерпретироваться как что-нибудь еще? Если это так, и если это имеет смысл в этом контексте, то компилятор должен рассмотреть синтаксическое выражение всего выражения как конструкцию объекта b.

Это, вероятно, основной момент, когда GCC и Clang не согласны:

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

Я не вижу, как это можно было бы интерпретировать как что-либо еще, кроме попытки построить временный тип A и вызвать его оператор вызова. Это не может быть объявлением функции, потому что если A интерпретируется как возвращаемый тип, тогда имя отсутствует (и наоборот).

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

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