Запутанная деталь о самом Vexing Parse - PullRequest
15 голосов
/ 10 августа 2011

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

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

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

Ответы [ 4 ]

12 голосов
/ 10 августа 2011

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

7 голосов
/ 10 августа 2011

Разве я уже говорил, что мне очень нравится 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, то есть именованный параметр функции.

6 голосов
/ 10 августа 2011

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

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

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

2 голосов
/ 10 августа 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]
...