关于最令人烦恼的解析的令人困惑的细节

发布于 2024-11-28 17:03:17 字数 381 浏览 0 评论 0原文

我的问题是如何将以下行解析为函数声明:

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

我了解最令人烦恼的解析的大部分细节以及为什么第二个临时迭代器可以解释为返回迭代器且不带参数的函数的类型,但我不明白的是为什么第一个临时迭代器可以解释为类型。它代表什么类型?我的想法是,这将是某种函数类型,但我看不出名称 cin 是如何使用的。它是否声明该参数是一个名为 cinistream_iterator?如果是这样,这是否意味着您可以任意将函数的参数名称括起来?如果是这样,为什么?

My question is how the following line can be parsed as a function declaration:

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

I understand most of the details of the Most Vexing Parse and why the second temporary iterator can be interpreted as a type that is a function returning an iterator and taking no arguments, but what I don't get is why the first temporary iterator can be interpreted as a type. What type does it represent? My thought is that it would be some sort of function type, but I can't see how the name cin gets used. Is it declaring that the parameter is an istream_iterator<int> named cin? If so, does that mean that you can arbitrarily parenthesize the names of arguments to functions? And if so, why?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

无法回应 2024-12-05 17:03:17

istream_iterator(cin)istream_iterator; 完全相同。 cin 但带有多余的括号。这种声明符语法是从 C 继承的,我认为甚至 C 的发明者(Ken Thompson?)也将其描述为错误。

istream_iterator<int>(cin) is exactly the same as istream_iterator<int> cin but with superfluous parens. This declarator syntax was inherited from C, and I think even the inventor of C (Ken Thompson?) described it as a mistake.

信愁 2024-12-05 17:03:17

我是否已经说过我(非常)喜欢 Clang ?

只需尝试以下(简化的代码)

#include <vector>

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

int main() {
  std::vector<int> v(int(i), int());
  foo(v);
}

在新重新命名的 LLVM Try Out 中(嗯,它只是从 llvm-gcc 变为铛)。

你会得到:

/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,即函数的命名参数。

Did I already said that I liked Clang (a lot) ?

Just try the following (simplified code)

#include <vector>

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

int main() {
  std::vector<int> v(int(i), int());
  foo(v);
}

In the newly rebrandished LLVM Try Out (well, it just went from llvm-gcc to clang).

And you get:

/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.

And therefore, @john is right, int(i) is interpreted as int i, ie a named parameter to the function.

噩梦成真你也成魔 2024-12-05 17:03:17

是的,它是参数名称。而且,是的,您可以添加一组括号,因为有时您必须这样做。

如果参数是函数指针,则需要这样写void (*f)()

编写标准的人没有花费宝贵的时间来准确指出允许或实际需要括号的情况,因此标准只是说您可以拥有它们。

Yes, it is the parameter name. And, yes you can add a set of parenthesis, because sometimes you have to.

If the parameter is a function pointer, void (*f)() you need to write it like that.

The people writing the standard have not spent their precious time pointing out exactly the cases where the parenthesis are allowed or actually required, so the standard just says that you can have them.

月下凄凉 2024-12-05 17:03:17

标准 (2003) 中有一个名为歧义解决的部分专门讨论此类语法。我想如果您自己阅读该部分,我不需要进一步解释,因为它非常清楚,有很多例子!

所以你开始吧:

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 - 由于函数样式转换和类型 ID 之间的相似性而产生的歧义可能会出现在不同的上下文中。这种歧义表现为函数式转换表达式和类型声明之间的选择。 解决方案是,任何在其语法上下文中可能是类型 ID 的构造都应被视为类型 ID。

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 运算符的操作数的 type-id 中会出现另一个歧义。 在这种情况下,需要在函数指针类型的参数声明和在声明符 ID 周围带有多余括号的参数声明之间进行选择。解决方案是将类型名称视为
简单类型说明符而不是声明符 ID。

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

There is a section called Ambiguity resolution in the Standard (2003) which is dedicated to such syntaxes. I think I don't need to explain it further if you read the section yourself, for its very clear with lots of examples!

So here you go:

8.2 Ambiguity resolution [dcl.ambig.res]

1 - The ambiguity arising from the similarity between a function-style cast and a declaration mentioned in 6.8 can also occur in the context of a declaration. In that context, the choice is between a function declaration with a redundant set of parentheses around a parameter name and an object declaration with a function-style cast as the initializer. Just as for the ambiguities mentioned in 6.8, the resolution is to consider any construct that could possibly be a declaration a declaration. [Note: a declaration can be explicitly disambiguated by a nonfunction-style cast, by a = to indicate initialization or by removing the redundant parentheses around the parameter name. ]

[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 - The ambiguity arising from the similarity between a function-style cast and a type-id can occur in different contexts. The ambiguity appears as a choice between a function-style cast expression and a declaration of a type. The resolution is that any construct that could possibly be a type-id in its syntactic context shall be considered a type-id.

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 - Another ambiguity arises in a parameter-declaration-clause of a function declaration, or in a type-id that is the operand of a sizeof or typeid operator, when a type-name is nested in parentheses. In this case, the choice is between the declaration of a parameter of type pointer to function and the declaration of a parameter with redundant parentheses around the declarator-id. The resolution is to consider the type-name as a
simple-type-specifier rather than a declarator-id.

[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]
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文