3

I'm a little bit supprised as i see this compilation error;

Here is the example:

class A
{
public:
  enum eA { eA1, eA2 };

  class B
  {
  public:
    B(eA e, int i = 0);
  };

  A(const B &b);
};

A::A(const B &b)
{
}

A::B::B(eA e, int i /*= 0*/)
{
}

int main()
{
  A::B b(A::eA1);        // OK
  A a0(b);               // OK
  A a1(A::B(A::eA1, 0)); // OK
  A a2(A::B(A::eA2));    //error C2751: 'A::eA2': the name of a function parameter cannot be qualified
  return 0;
}

As pointed in comments, A a2(A::B(A::eA2)); doesn't compile. Why? I'm not asking how to compile it. Why it doesn't compile?

It compiles, if first parameter type of class-B is not from class-A. for example with int it compiles.

  • I would say this is a Most Vexing Parse issue (your error line attempts to declare `a2` as a function returning `A` taking one parameter of typ `A::B` named `A::eA2`) ... although not sure yet whether the presence of `A::` should make it fall back to being an object declaration, versus giving a declaration syntax error – M.M May 19 '17 at 10:20
  • 1
    This should not be marked as duplicate as this is valid code that [compiles file with gcc](http://ideone.com/rXmavE). This seem to be VS bug of some kind. As a workaround you can use uniform initialization when constructing variables like this: `A a2{A::B(A::eA2)};` – user7860670 May 19 '17 at 10:25
  • @M.M I was reading right now another question [link](http://stackoverflow.com/questions/44045257/c-compiler-error-c2751-what-exactly-causes-it) which looks like my problem, but i still don't understand, why it compiles if the type of first B-Constructor is int. – Yusuf R. Karagöz May 19 '17 at 10:25
  • @M.M if B-Constructor were `B(int i1, int i2 = 0);`, than it compiles `A a(A::B(1));` – Yusuf R. Karagöz May 19 '17 at 10:31
  • 1
    @YusufR.Karagöz you may want to open [Microsoft connect ticket](https://connect.microsoft.com/VisualStudio) as this problem seems to be present in VS2017 as well. – user7860670 May 19 '17 at 10:33
  • 1
    @YusufR.Karagöz `1` is not an identifier so there is no ambiguity – M.M May 19 '17 at 10:35
  • @VTT I think this is not a compiler bug, so reporting a bug would be a bad idea – M.M May 19 '17 at 10:35
  • @VVT you are right, but i think the question is not duplicate as marked here. the question can be closed but it is not an absolute duplicate. – Yusuf R. Karagöz May 19 '17 at 10:36
  • 1
    reopened, although it is a MVP issue this is more complicated than what is answered in the suggested duplicate, which does not cover the case of the innermost id-expression containing `::` – M.M May 19 '17 at 10:37
  • @M.M when it is not a bug, how does it compiles with gcc? Or do we have any warning with gcc, instead of error? – Yusuf R. Karagöz May 19 '17 at 10:37
  • gcc might be bugged – M.M May 19 '17 at 10:38
  • 1
    @M.M. Well, since gcc compiles it fine and VS not then it is a bug in either gcc or VS (and I typically tend to blame VS). – user7860670 May 19 '17 at 10:39
  • 1
    Added MVP/LL tags and removed inner-class tag; the inner class is a red herring and you could reproduce the same issue with B not being an inner class. (The enum must still be inside A though for this particular case) – M.M May 19 '17 at 10:57
  • @M.M Should we also correct the question title? – Yusuf R. Karagöz May 19 '17 at 11:00
  • It would be fine with or without IMO , maybe you could instead mention "qualified enum as argument" instead of "default parameter" (which is another red herring) – M.M May 19 '17 at 11:04

1 Answers1

2

This is a Most Vexing Parse issue. The canonical case would be:

T t( U(x) );

where T and U are previously known to be type names. This could be parsed in two valid ways:

  • a declaration of t as an object of type T, with the initializer being the expression U(x), a function-style cast of the variable x to a temporary U
  • a declaration of t as a function returning T, with 1 parameter of type U and name x; and there are redundant parentheses around x. (Declarators may be parenthesized).

The text in the Standard for disambiguating is in [dcl.ambig.res]/1. What it says is that parsing this code hinges on whether U(x) would be a declaration or an expression, and then refers to [stmt.ambig].

In [stmt.ambig]/2 there is a clarifying example. I won't reproduce the full example here (you can look it up in a standards draft) but the accompanying text is:

If the statement cannot syntactically be a declaration, there is no ambiguity

[...] This is of course ill-formed for semantic reasons, but that does not affect the syntactic analysis. In those cases the statement is a declaration.

What this is trying to say is that if the code can be matched to the rules in the language grammar for a declaration, then it is a declaration, even if the code subsequently falls foul of a semantic rule.


Looking at your variation now, where the inner code is (simplified) U(A::e) where e is an enumerator in the scope of A.

I believe this code does still match the grammar rule for a declaration. See [dcl.decl]/4 grammar specification (selected parts):

noptr-declarator: declarator-id attribute-specifier-seqopt

declarator-id: ...opt id-expression

and id-expression can be qualified-id or unqualified-id, and finally, A::e is a qualified-id.

Even though there is a semantic rule [dcl.meaning]/1 that the declarator-id can only be a qualified-id in certain contexts, excluding this case, that's not a grammar rule.

So I would say VC is correct to reject the code.


To fix the code, assuming the intent is for a2 to be an object declaration, you can use the same techniques as mentioned on the canonical MVP thread:

A a2{A::B(A::eA2)};    // braced initialization

A a2((A::B(A::eA2)));  // function declarations can't have extra parentheses around the entire parameter declaration, so this cannot be a function declaration 
Community
  • 1
  • 1
M.M
  • 138,810
  • 21
  • 208
  • 365