117

As an interesting follow-up (not of big practical importance though) to my previous question: Why does C++ allow us to surround the variable name in parentheses when declaring a variable?

I found out that combining the declaration in parentheses with injected class name feature may lead to surprising results regarding compiler behavior.

Take a look at the following program:

#include <iostream>
struct B
{
};

struct C
{
  C (){ std::cout << "C" << '\n'; }
  C (B *) { std::cout << "C (B *)" << '\n';}
};

B *y = nullptr;
int main()
{
  C::C (y);
}
  1. Compiling with g++ 4.9.2 gives me the following compilation error:

    main.cpp:16:10: error: cannot call constructor 'C::C' directly [-fpermissive]
    
  2. It compiles successfully with MSVC2013/2015 and prints C (B *)

  3. It compiles successfully with clang 3.5 and prints C

So obligatory question is which one is right? :)

(I strongly swayed towards clang version though and msvc way to stop declaring variable after just changing type with technically its typedef seems kind of weird)

Community
  • 1
  • 1
Predelnik
  • 5,066
  • 2
  • 24
  • 36
  • 3
    `C::C y;` doesn't make sense, right? Neither does `C::C (y);` At first I thought this was an instance of Most-Vexing-Parse http://stackoverflow.com/questions/tagged/most-vexing-parse, but now I think it's just undefined behavior meaning all three compilers are "right." – Dale Wilson Apr 16 '15 at 17:09
  • 4
    #3 clang is definitely wrong, #2 msvc is too permissive and #1 g++ is right ( (I guess) –  Apr 16 '15 at 17:09
  • 1
    Related: http://stackoverflow.com/questions/26558507/injected-class-names-of-class-templates – David G Apr 16 '15 at 17:09
  • 8
    `C::C` does not name a type it names a function, so GCC is right imo. – Galik Apr 16 '15 at 17:15
  • 11
    [Filed as a bug on connect](https://connect.microsoft.com/VisualStudio/feedback/details/1253133) – Mgetz Apr 16 '15 at 17:56
  • 11
    [Filed as a clang bug](https://llvm.org/bugs/show_bug.cgi?id=23254). – Cornstalks Apr 16 '15 at 19:41
  • 1
    ICC, FWIW, agrees with GCC on this: http://goo.gl/CkPZmW – LThode Apr 16 '15 at 19:52

2 Answers2

92

GCC is correct, at least according to C++11 lookup rules. 3.4.3.1 [class.qual]/2 specifies that, if the nested name specifier is the same as the class name, it refers to the constructor not the injected class name. It gives examples:

B::A ba;           // object of type A
A::A a;            // error, A::A is not a type name
struct A::A a2;    // object of type A

It looks like MSVC misinterprets it as function-style cast expression creating a temporary C with y as a constructor parameter; and Clang misinterprets it as a declaration of a variable called y of type C.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • 2
    Yes, 3.4.3.1/2 is the key. Good job! – Lightness Races in Orbit Apr 16 '15 at 17:26
  • It says "In a lookup in which function names are not ignored". It seems to me that in the examples given, in particular `A::A a;`, function names should be ignored - or not? – Columbo Apr 16 '15 at 17:26
  • 1
    Going by the numbering in N4296, the key is really 3.4.3.1/2.1: "if the name specified after the nested-name-specifier, when looked up in C, is the injected-class-name of C [ ...] the name is instead considered to name the constructor of class C." Mike's summary is a little over-simplified though--for example, a typedef of the class-name inside the class would allow a nested name specifier different from the class name to still refer to the class name, so it would still refer to the ctor. – Jerry Coffin Apr 16 '15 at 17:29
  • _"It says 'In a lookup in which function names are not ignored'."_ Only since C++14. I can't speak to how this may affect this answer as I am not familiar with the "ignoring function names" terminology, but the given example in that paragraph has not changed. So I'm going to say "it doesn't". – Lightness Races in Orbit Apr 16 '15 at 17:32
  • Can someone try this on VS2015 so I can file the bug on connect? – Mgetz Apr 16 '15 at 17:46
  • 2
    @Mgetz: From the question: _"It compiles successfully with MSVC2013/2015 and prints `C (B *)`"_. – Lightness Races in Orbit Apr 16 '15 at 17:52
  • @Columbo No, it isn't ignored. (If function names were ignored, then it would work even if the type name was hidden by a function name.) – T.C. Apr 16 '15 at 17:53
  • 2
    For completeness this should clarify whether it is ill-formed with diagnostic required, or ill-formed with no diagnostic required. If the latter then all compilers are "right". – M.M Apr 23 '15 at 03:41
16

G++ is correct as it gives an error. Because the constructor could not be called directly in such a format without new operator. And although your code calls C::C, it looks like an constructor call. However, according to the C++11 standard 3.4.3.1, this is not a legal function call, or a type name (see Mike Seymour's answer).

Clang is wrong since it even does not call the correct function.

MSVC is something reasonable, but still it does not follow the standard.

Community
  • 1
  • 1
Kun Ling
  • 2,211
  • 14
  • 22
  • 2
    What does `new` operator change? – Neil Kirk Apr 16 '15 at 17:17
  • 1
    @NeilKirk: A lot, for people who think that `new B(1,2,3)` is some kind of "direct constructor call" (which, of course, it is not) as distinct from the temporary instantiation `B(1,2,3)` or the declaration `B b(1,2,3)`. – Lightness Races in Orbit Apr 16 '15 at 17:24
  • @LightningRacisinObrit How would you describe what `new B(1,2,3)` is? – user2030677 Apr 16 '15 at 17:56
  • 1
    @user2030677: A new-expression, using the keyword `new`, a type name, and a constructor argument list. It's still not a "direct constructor call". – Lightness Races in Orbit Apr 16 '15 at 18:10
  • "Clang is wrong since it even does not call the correct function.": I think (because the OP's remark about parentheses in declarations) that Clang interpretes `C::C (y);` as `C::C y;`, i.e. a defintion of a variable y of type C (using the injected type C::C while erroneously ignoring the increasingly insane language specification's 3.4.1,2 which makes C::C the constructor). That is not quite a glaring error as you seem to think, imo. – Peter - Reinstate Monica Apr 16 '15 at 20:57
  • @LightnessRacesinOrbit: If new B(1,2,3) isn't some kind of direct constructor call then exactly what it is? – Destructor Dec 17 '15 at 08:47
  • @LightnessRacesinOrbit: See this program also: http://ideone.com/2HjCeZ. Constructor is being clearly called in the program when I write new Test(3,6). I am confused due to ur comments. Will you please clear the doubts? – Destructor Dec 17 '15 at 09:01
  • @PravasiMeet: That the constructor is invoked doesn't mean `new B(1,2,3)` is "a direct constructor" call, any more than any other means of constructing an object is "a direct constructor call". You are surely aware that `new` does other things? – Lightness Races in Orbit Dec 17 '15 at 10:47