Глобальная инициализация с временным функциональным объектом
Следующий код
#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?
Ответы
Ответ 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 неверен.