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