Запутанная деталь о самом неприятном разборе

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

vector<int> v(istream_iterator<int>(cin), istream_iterator<int>());

Я понимаю большинство деталей самого раздражающего синтаксического анализа и почему второй временный итератор может быть интерпретирован как тип, являющийся функцией, возвращающей итератор и не принимающей аргументов, но чего я не понимаю, так это того, почему первый временный итератор может быть интерпретируется как тип. Какой тип он представляет? Я думаю, что это будет какой-то тип функции, но я не понимаю, как используется имя cin. Объявляется ли это параметром istream_iterator<int> с именем cin? Если да, значит ли это, что вы можете произвольно заключать в скобки имена аргументов функций? И если да, то почему?


person templatetypedef    schedule 10.08.2011    source источник


Ответы (4)


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

person john    schedule 10.08.2011
comment
istream_iterator<int>(cin) НЕ совпадает с istream_iterator<int> cin. - person Nawaz; 10.08.2011
comment
Это как параметр функции. - person Bo Persson; 10.08.2011
comment
Я думаю, что они в контексте заданного вопроса (возможно, я не должен был говорить точно). Хотите уточнить? - person john; 10.08.2011
comment
@john: istream_iterator<int>(cin) такое же, как istream_iterator<int> anonymous(cin). То есть cin является аргументом конструктора istream_iterator<int>. - person Nawaz; 10.08.2011
comment
@Nawaz: istream_iterator<int>(cin) интерпретируется не как выражение, а как объявление параметра, в этом суть вопроса. - person john; 10.08.2011
comment
@john: Что это должно означать? - person Nawaz; 10.08.2011
comment
@Nawaz: я имею в виду, что код 'vector‹int› v(istream_iterator‹int›(cin), istream_iterator‹int›());' интерпретируется как прототип функции, потому что частично istream_iterator<int>(cin) можно интерпретировать как объявление параметра. Я что-то упустил здесь? Я хочу, чтобы ты сказал мне, если я. - person john; 10.08.2011
comment
@Nawaz: Я думаю, вы могли бы быть более конструктивными в своих комментариях. - person john; 10.08.2011
comment
Или, может быть, @Nawaz, ты ошибаешься. Вы проверили это? Что компилятор считает переменной? Простой тест: создайте функцию, которая принимает int в качестве аргумента, и в main добавьте это определение вектора (фактически объявление функции), а затем попытайтесь передать v в функцию. В сообщении об ошибке будет указано, что это за тип в соответствии с интерпретацией компилятора. - person David Rodríguez - dribeas; 10.08.2011
comment
@Matthieu: Нет. Он говорит о istream_iterator<int>(cin), который НЕ вызывает проблемы. Это istream_iterator<int>()вызывает проблему. Ваш случай другой. В вашем случае int i интерпретируется как объявление параметра, потому что i не объявлен объектом в области. А int() — это тип указателя на функцию точно так же, как istream_iterator<int>(). См. это: ideone.com/p1Xx5 ... и это: ideone.com/d4kep ... и сравните их! - person Nawaz; 10.08.2011
comment
Дэвид: См. мой предыдущий комментарий к @Matthieu. Особенно эти: ideone.com/p1Xx5 .. и ideone.com/d4kep .. - person Nawaz; 10.08.2011
comment
@Nawaz, ты неправильно понял. Я не говорил, что istream_iterator<int>(cin) вызывает проблему. ОП специально спросил, как istream_iterator<int>(cin) можно интерпретировать как объявление параметра. Так вот на этот вопрос я ответил. - person john; 10.08.2011
comment
@john: Хм... Понятно. Теперь я понимаю вашу точку зрения. Спасибо за разъяснения. :-) - person Nawaz; 10.08.2011
comment
@Nawaz: Нам известно, что проблема спровоцирована istream_iterator<int>(), в ответе @john просто говорится, что эта строка может интерпретироваться как объявление, поскольку istream_iterator<int>(cin) может интерпретироваться как функция параметр в контексте объявления функции. Я также хотел бы отметить, что в этом случае не имеет значения, находится ли i в области видимости (или нет). - person Matthieu M.; 10.08.2011
comment
@Матье: Да. Теперь я понимаю проблему. Спасибо за все разъяснения. :-) - person Nawaz; 10.08.2011
comment
@Nawaz: нет проблем, я тоже не всегда получаю все вопросы при первом чтении (я виню свое понимание английского... но в большинстве случаев это просто я читаю слишком поспешно). - person Matthieu M.; 10.08.2011
comment
Да, мой ответ мог бы включать больше контекста из вопроса ОП. - person john; 10.08.2011
comment
@ Наваз, вы ошибаетесь в том, что istream_iterator‹int›(cin) такой же, как istream_iterator‹int› анонимный (cin). в общем случае тоже. Он объявит cin. Например, int(cin); эквивалентно int cin;. - person Johannes Schaub - litb; 10.08.2011
comment
@Johannes: Это на самом деле зависит. Но я понимаю это сейчас, зная контекст. :-) - person Nawaz; 10.08.2011
comment
@Наваз, контекст не нужен. Это всегда объявление cin, это не может быть временный объект. Вы ошиблись. - person Johannes Schaub - litb; 10.08.2011
comment
@Johannes: это зависит от второго параметра. Если вы используете в этом дополнительные скобки, то istream_iterator<int>(cin) будет анонимным объектом и даст результат, как и предполагалось. - person Nawaz; 10.08.2011
comment
@Nawaz нет второго параметра в int(cin);, а также нет в istream_iterator<int>(cin);. Вы как-то подчеркиваете второй параметр именно этого примера в вопросе, но особой роли в различении чего-либо он не играет. Вы также можете заключить в скобки первый параметр. vector<int> v((istream_iterator<int>(cin)), istream_iterator<int>());. Ваша позиция глубоко сбивает с толку, и похоже, что, несмотря на то, что вы процитировали большое количество текста Стандартов, вы не поняли проблему. - person Johannes Schaub - litb; 10.08.2011
comment
@Johannes: Я говорю об этом: vector<int> v(istream_iterator<int>(cin), istream_iterator<int>());, в котором я говорю, что если вы используете дополнительные скобки для istream_iterator<int>(), то первый будет объектом. - person Nawaz; 10.08.2011
comment
@john Деннис Ритчи — изобретатель C. - person L. F.; 28.03.2019
comment
Кен Томпсон — создатель B и воинствующий критик C и C++ по сей день. - person Swift - Friday Pie; 06.06.2021

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

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

#include <vector>

void foo(std::vector<int>);

int main() {
  std::vector<int> 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<int> 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, int (*)())'
     to 'std::vector<int>' for 1st argument
void foo(std::vector<int>);
     ^
3 diagnostics generated.

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

person Matthieu M.    schedule 10.08.2011

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

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

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

person Bo Persson    schedule 10.08.2011
comment
Можете ли вы привести пример, если это было бы необходимо? - person templatetypedef; 10.08.2011
comment
int *a[10] — это массив из 10 указателей int, но int (*a)[10] — это указатель на массив из 10 целых чисел (надеюсь, я понял это правильно). - person john; 10.08.2011
comment
Ах, так что это не столько вам нужно, сколько требуется некоторым типам. Я неправильно истолковал ваше замечание о том, что существуют типы, для которых ошибка времени компиляции не включает дополнительный набор скобок, которые в противном случае не были бы необходимо. - person templatetypedef; 10.08.2011

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

Итак, вот:

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

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

[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 <cstddef>

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 <class T>
struct S {
    T *p;
};
S<int()> x;  // type-id
S<int(1)> 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, когда имя типа заключено в круглые скобки. В этом случае выбор делается между объявлением параметра типа указателя на функцию и объявлением параметра с лишними круглыми скобками вокруг идентификатора декларатора. Решение состоит в том, чтобы рассматривать имя типа как спецификатор простого типа, а не как идентификатор декларатора.

[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]
person Nawaz    schedule 10.08.2011