A confusing detail about the Most Vexing Parse

旧巷老猫 提交于 2019-11-25 22:49:35

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.

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.

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.

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]
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!