Программа скомпилирована по-разному в трех основных компиляторах на С++. Какой из них прав?

Как интересное продолжение (не большое практическое значение) к моему предыдущему вопросу: Почему С++ позволяет нам окружать имя переменной в круглых скобках при объявлении переменной?

Я выяснил, что объединение объявления в круглых скобках с введенным именем класса может привести к неожиданным результатам относительно поведения компилятора.

Взгляните на следующую программу:

#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);
}
  • Компиляция с g++ 4.9.2 дает мне следующую ошибку компиляции:

    main.cpp:16:10: error: cannot call constructor 'C::C' directly [-fpermissive]
    
  • Он успешно компилируется с MSVC2013/2015 и печатает C (B *)

  • Он успешно компилируется с clang 3.5 и печатает C

Итак, обязательный вопрос: какой из них прав?:)

(Я сильно склонялся к версии clang, хотя и способ msvc, чтобы остановить объявление переменной после простого изменения типа с технически его typedef кажется странным)

Ответ 1

GCC корректен, по крайней мере, согласно правилам поиска С++ 11. 3.4.3.1 [class.qual]/2 указывает, что если вложенный спецификатор имени совпадает с именем класса, он ссылается на конструктор, а не на имя введенного класса. Он дает примеры:

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

Похоже, что MSVC неправильно истолковывает это как выражение выражения стиля функции, создавая временный C с y как параметр конструктора; и Кланг неправильно истолковывает это как объявление переменной с именем y типа C.

Ответ 2

g++ корректен, поскольку он дает ошибку. Поскольку конструктор не может быть вызван непосредственно в таком формате без оператора new. И хотя ваш код вызывает C::C, он выглядит как вызов конструктора. Однако, согласно стандарту С++ 11 3.4.3.1, это не вызов юридической функции или имя типа (см. Ответ Майка Сеймура).

Clang неверен, так как он даже не вызывает правильную функцию.

MSVC - это нечто разумное, но все же оно не соответствует стандарту.