Als interessante Folge (allerdings nicht von großer praktischer Bedeutung) zu meiner vorherigen Frage: Warum erlaubt C ++ uns, den Variablennamen beim Deklarieren einer Variablen in Klammern zu setzen?
Ich fand heraus, dass das Kombinieren der Deklaration in Klammern mit der Funktion für injizierte Klassennamen zu überraschenden Ergebnissen hinsichtlich des Compilerverhaltens führen kann.
Schauen Sie sich folgendes Programm an:
#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);
}
Das Kompilieren mit g ++ 4.9.2 führt zu folgendem Kompilierungsfehler:
main.cpp:16:10: error: cannot call constructor 'C::C' directly [-fpermissive]
Es wird erfolgreich mit MSVC2013 / 2015 kompiliert und gedruckt
C (B *)
Es wird erfolgreich mit clang 3.5 kompiliert und gedruckt
C
Die obligatorische Frage ist also, welche richtig ist. :) :)
(Ich schwankte jedoch stark in Richtung Clang-Version und msvc Weg, um die Deklaration von Variablen zu stoppen, nachdem ich nur den Typ mit technisch geändert habe, scheint sein Typedef irgendwie seltsam)
quelle
C::C y;
macht keinen Sinn, oder? Auch nichtC::C (y);
Zuerst dachte ich , das war eine Instanz von Most-Vexing-Parse stackoverflow.com/questions/tagged/most-vexing-parse , aber jetzt denke ich , es ist nur nicht definiertes Verhalten bedeutet , alle drei Compiler sind „richtig.“C::C
benennt keinen Typ, sondern eine Funktion, daher ist GCC imo richtig.Antworten:
GCC ist korrekt, zumindest gemäß den C ++ 11-Suchregeln. 3.4.3.1 [class.qual] / 2 gibt an, dass der verschachtelte Namensbezeichner, wenn er mit dem Klassennamen identisch ist, auf den Konstruktor und nicht auf den injizierten Klassennamen verweist. Es gibt Beispiele:
Es sieht aus wie MSVC misinterprets es als funktions Casts Ausdruck eine temporäre Erstellung
C
mity
als Konstruktor Parameter; und Clang interpretiert es falsch als Deklaration einer Variableny
vom TypC
.quelle
A::A a;
Funktionsnamen ignoriert werden sollten - oder nicht?C (B *)
" .G ++ ist korrekt, da es einen Fehler gibt. Weil der Konstruktor ohne
new
Operator nicht direkt in einem solchen Format aufgerufen werden konnte . Und obwohl Ihr Code aufruftC::C
, sieht es aus wie ein Konstruktoraufruf. Gemäß dem C ++ 11-Standard 3.4.3.1 ist dies jedoch kein zulässiger Funktionsaufruf oder Typname ( siehe Antwort von Mike Seymour ).Clang ist falsch, da es nicht einmal die richtige Funktion aufruft.
MSVC ist etwas Vernünftiges, aber es folgt immer noch nicht dem Standard.
quelle
new
Bediener?new B(1,2,3)
eine Art "direkter Konstruktoraufruf" ist (was natürlich nicht der Fall ist), der sich von der temporären InstanziierungB(1,2,3)
oder der Deklaration unterscheidetB b(1,2,3)
.new B(1,2,3)
ist?new
, einen Typnamen und eine Konstruktorargumentliste verwendet. Es ist immer noch kein "direkter Konstruktoraufruf".C::C (y);
wieC::C y;
, dh eine Defintion einer Variablen y vom Typ C (die injizierte Typ C unter Verwendung von : : C, während fälschlicherweise die zunehmend verrückte Sprachspezifikation 3.4.1.2 ignoriert wird, die C :: C zum Konstruktor macht). Das ist kein eklatanter Fehler, wie Sie zu denken scheinen, imo.