Predelnik
Predelnik

Reputation: 5246

Program being compiled differently in 3 major C++ compilers. Which one is right?

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)

Upvotes: 117

Views: 5129

Answers (2)

Kun Ling
Kun Ling

Reputation: 2219

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.

Upvotes: 16

Mike Seymour
Mike Seymour

Reputation: 254431

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.

Upvotes: 92

Related Questions