Jako ciekawa kontynuacja (choć nie ma to dużego znaczenia praktycznego) mojego poprzedniego pytania: Dlaczego C ++ pozwala nam umieszczać nazwę zmiennej w nawiasach podczas deklarowania zmiennej?
Dowiedziałem się, że połączenie deklaracji w nawiasach z właściwością wstrzykniętej nazwy klasy może prowadzić do zaskakujących wyników w zachowaniu kompilatora.
Spójrz na następujący 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);
}
Kompilacja z g ++ 4.9.2 daje następujący błąd kompilacji:
main.cpp:16:10: error: cannot call constructor 'C::C' directly [-fpermissive]
Kompiluje się pomyślnie z MSVC2013 / 2015 i drukuje
C (B *)
Kompiluje się pomyślnie z clang 3.5 i drukuje
C
Tak więc obowiązkowe pytanie brzmi, który z nich jest właściwy? :)
(Silnie kołysałem się w kierunku wersji clang i sposób, w jaki msvc przestał deklarować zmienną po zmianie typu, a technicznie jego typedef wydaje się trochę dziwny)
źródło
C::C y;
nie ma sensu, prawda? Ani też.C::C (y);
Na początku myślałem, że to wystąpienie Most-Vexing-Parse stackoverflow.com/questions/tagged/most-vexing-parse , ale teraz myślę, że jest to po prostu niezdefiniowane zachowanie, co oznacza, że wszystkie trzy kompilatory mają „rację”.C::C
nie nazywa typu, nazywa funkcję, więc GCC ma rację imo.Odpowiedzi:
GCC jest poprawne, przynajmniej zgodnie z regułami wyszukiwania w C ++ 11. 3.4.3.1 [class.qual] / 2 określa, że jeśli zagnieżdżony specyfikator nazwy jest taki sam jak nazwa klasy, to odwołuje się do konstruktora, a nie do wstrzykniętej nazwy klasy. Podaje przykłady:
Wygląda na to, że MSVC błędnie interpretuje je jako wyrażenie rzutowania w stylu funkcji, tworząc tymczasowe
C
zy
parametrem konstruktora; a Clang błędnie interpretuje to jako deklarację zmiennej o nazwiey
typC
.źródło
A::A a;
należy zignorować nazwy funkcji - czy nie?C (B *)
” .G ++ jest poprawne, ponieważ daje błąd. Ponieważ konstruktora nie można wywołać bezpośrednio w takim formacie bez
new
operatora. I chociaż Twój kod wywołujeC::C
, wygląda to jak wywołanie konstruktora. Jednak zgodnie ze standardem 3.4.3.1 języka C ++ 11 nie jest to legalne wywołanie funkcji ani nazwa typu ( patrz odpowiedź Mike'a Seymoura ).Clang jest błędny, ponieważ nawet nie wywołuje prawidłowej funkcji.
MSVC to coś rozsądnego, ale nadal nie spełnia standardów.
źródło
new
operator?new B(1,2,3)
jest to coś w rodzaju „bezpośredniego wywołania konstruktora” (którym oczywiście nie jest) w odróżnieniu od tymczasowej instancjiB(1,2,3)
lub deklaracjiB b(1,2,3)
.new B(1,2,3)
jest?new
, nazwę typu i listę argumentów konstruktora. To wciąż nie jest „bezpośrednie wywołanie konstruktora”.C::C (y);
jakoC::C y;
, tj. Definicję zmiennej y typu C (używając wstrzykniętego typu C: : C, podczas gdy błędnie ignoruje coraz bardziej szaloną specyfikację języka 3.4.1,2, co sprawia, że C :: C jest konstruktorem). To niezbyt rażący błąd, jak ci się wydaje, imo.