Сложная информация о самом развязном параде

Мой вопрос заключается в том, как следующую строку можно проанализировать как объявление функции:

vector v(istream_iterator(cin), istream_iterator()); 

Я понимаю большинство деталей самого Vexing Parse и почему второй временный iterator можно интерпретировать как тип, который возвращает функцию iteratorа и не принимает аргументов, но то, что я не получаю, – это то, почему первый временный iterator может быть интерпретируется как тип. Какой тип он представляет? Моя мысль заключается в том, что это будет какой-то тип функции, но я не вижу, как используется имя cin . istream_iterator ли он, что параметр istream_iterator named cin ? Если это так, означает ли это, что вы можете произвольно заключить в скобки имена аргументов функций? И если да, то почему?

istream_iterator(cin) точно такой же, как istream_iterator cin но с избыточными parens. Этот синтаксис декларатора был унаследован от C, и я думаю, что даже изобретатель C (Кен Томпсон?) Описал это как ошибку.

Я уже говорил, что мне нравится Кланг (много)?

Просто попробуйте следующий (упрощенный код)

 #include  void foo(std::vector); int main() { std::vector v(int(i), int()); foo(v); } 

В недавно обновленном LLVM Try Out (ну, он просто пошел от llvm-gcc до clang).

И вы получаете:

 /tmp/webcompile/_21483_0.cc:6:21: warning: parentheses were disambiguated as a function declarator std::vector v(int(i), int()); ^~~~~~~~~~~~~~~ /tmp/webcompile/_21483_0.cc:7:3: error: no matching function for call to 'foo' foo(v); ^~~ /tmp/webcompile/_21483_0.cc:3:6: note: candidate function not viable: no known conversion from 'std::vector (int, int (*)())' to 'std::vector' for 1st argument void foo(std::vector); ^ 3 diagnostics generated. 

И поэтому @john прав, int(i) интерпретируется как int i , т.е. именованный параметр функции.

Да, это имя параметра. И, да, вы можете добавить набор круглых скобок, потому что иногда вам нужно.

Если параметр является указателем на функцию, void (*f)() вам нужно написать его так.

Люди, пишущие стандарт, не потратили свое драгоценное время, указав именно те случаи, когда скобки разрешены или действительно необходимы, поэтому стандарт просто говорит, что вы можете их использовать.

В стандарте (2003) есть раздел под названием « Ambiguity resolution посвященный таким синтаксисам. Думаю, мне не нужно объяснять это дальше, если вы сами читаете раздел, потому что он очень ясен с большим количеством примеров!

Итак, вот вы идете:

8.2 Разрешение неоднозначности [dcl.ambig.res]

1 – Неопределенность, возникающая из-за сходства между приведением в стиле функции и декларацией, упомянутой в 6.8, может также возникать в контексте декларации. В этом контексте выбор осуществляется между объявлением функции с избыточным набором круглых скобок вокруг имени параметра и объявлением объекта с использованием функции-стиля в качестве инициализатора. Так же как и для двусмысленностей, упомянутых в 6.8, резолюция состоит в том, чтобы рассмотреть любую конструкцию, которая может быть объявлением декларацией. [Примечание: объявление может быть явно устранено с помощью неэффективного стиля при помощи a = для указания инициализации или путем удаления избыточных круглых скобок вокруг имени параметра. ]

 [Example: struct S { S(int); }; void foo(double a) { S w(int(a)); // function declaration S x(int()); // function declaration S y((int)a); // object declaration S z = int(a); // object declaration } —end example] 

2 – Неоднозначность, возникающая из-за сходства между приведением в стиле функции и идентификатором типа, может возникать в разных контекстах. Неоднозначность представляется как выбор между выражением выражения в стиле функции и объявлением типа. Разрешение заключается в том, что любая конструкция, которая может быть идентификатором типа в ее синтаксическом контексте, считается идентификатором типа.

 3- [Example: #include  char *p; void *operator new(size_t, int); void foo() { const int x = 63; new (int(*p)) int; // new-placement expression new (int(*[x])); // new type-id } //4 - For another example, template  struct S { T *p; }; S x; // type-id S y; // expression (ill-formed) //5 - For another example, void foo() { sizeof(int(1)); // expression sizeof(int()); // type-id (ill-formed) } //6 - For another example, void foo() { (int(1)); //expression (int())1; //type-id (ill-formed) } —end example] соединение 3- [Example: #include  char *p; void *operator new(size_t, int); void foo() { const int x = 63; new (int(*p)) int; // new-placement expression new (int(*[x])); // new type-id } //4 - For another example, template  struct S { T *p; }; S x; // type-id S y; // expression (ill-formed) //5 - For another example, void foo() { sizeof(int(1)); // expression sizeof(int()); // type-id (ill-formed) } //6 - For another example, void foo() { (int(1)); //expression (int())1; //type-id (ill-formed) } —end example] 

7 – Еще одна двусмысленность возникает в объявлении параметра-объявления объявления функции или в идентификаторе типа, который является операндом оператора sizeof или typeid, когда имя типа вложено в круглые скобки. В этом случае выбор осуществляется между объявлением параметра указателя типа на функцию и объявлением параметра с резервными скобками вокруг идентификатора declarator. Резолюция должна учитывать имя типа как простой тип-спецификатор, а не идентификатор объявления.

 [Example: class C { }; void f(int(C)) { } // void f(int (*fp)(C c)) { } // not: void f(int C); int g(C); void foo() { f(1); //error: cannot convert 1 to function pointer f(g); //OK } //For another example, class C { }; void h(int *(C[10])); // void h(int *(*_fp)(C _parm[10])); // not: void h(int *C[10]); —end example] 
Давайте будем гением компьютера.