Dlaczego dozwolone są nadmiarowe kwalifikatory nazw klas?
Natknąłem się na taki kod:
struct A {
A() {}
A(int) {}
};
struct B : A {
void init(int i);
};
void B::init(int i) {
A::A(i); // what is this?
}
int main() {
B b;
b.init(2);
}
Skompilował się i uruchomił przy użyciu VC11 beta bez błędów i ostrzeżeń przy / W4.
Widocznym celem jest wywołanie B :: init w celu ponownego zainicjowania podobiektu podstawowego B A. Uważam, że faktycznie analizuje jako deklarację zmiennej dla nowej zmiennej o nazwiei
z typemA
. Kompilowanie za pomocą clang powoduje diagnostykę:
ConsoleApplication1.cpp:11:14: warning: declaration shadows a local variable
A::A(i);
^
ConsoleApplication1.cpp:10:22: note: previous declaration is here
void B::init(int i) {
^
ConsoleApplication1.cpp:11:14: error: redefinition of 'i' with a different type
A::A(i);
^
ConsoleApplication1.cpp:10:22: note: previous definition is here
void B::init(int i) {
^
Ciekawe wydaje się, że do tego typu można odwołać się do zbędnych kwalifikacji klasowych.
Również,A::A(i)
wydaje się być inaczej analizowany przez VS11 i clang / gcc. Jeśli zrobięA::A(b)
clang i gcc tworzą zmiennąb
typuA
używając domyślnego konstruktora. Błędy VS11 w tym powiedzeniub
jest nieznanym identyfikatorem. VS11 wydaje się parsowaćA::A(i)
jako stworzenie tymczasowegoA
za pomocą konstruktoraA::A(int)
zi
jako parametr. Po wyeliminowaniu nadmiarowego kwalifikatora VS analizuje źródło jako deklarację zmiennej, jak clang i gcc, i generuje podobny błąd dotyczący przesłaniania zmienneji
.
Ta różnica w parsowaniu wyjaśnia, dlaczego VS11 zakrztusi się więcej niż jednym dodatkowym kwalifikatorem;A::A::A::A(i)
i dlaczego, biorąc pod uwagę, że clang i gcc mogą akceptować jeden dodatkowy kwalifikator, dowolna liczba więcej niż jedna dodatkowa ma taki sam wynik jak jeden dodatkowy.
Oto kolejny przykład z nadmiarowymi kwalifikatorami w innym kontekście. Wszystkie kompilatory wydają się analizować to jako tymczasową konstrukcję:
class Foo {};
void bar(Foo const &) {}
int main() {
bar(Foo::Foo());
}
Dlaczego dozwolone są w ogóle nadmiarowe kwalifikatory?Istnieją pewne konteksty, do których można odwoływać się do konstruktorów, takie jak składnia dziedziczenia konstruktorów (class D : B { using B::B; };
) ale VS wydaje się pozwalać na to gdziekolwiek. Czy VS jest błędny i czy clang i gcc są dokładnie analizowane w ten sposób?Wiem, że VS jest wciąż dość spóźniony pod względem zgodności ze standardami, ale wydaje mi się nieco zaskakujące, że nowoczesne, aktywnie opracowane kompilatory mogą być tak rozbieżne, w tym przypadku rozwiązując nadmiarowy kwalifikator jako nazwę konstruktora (chociaż konstruktory nie mają nazw) a rozwiązywanie redundantnych kwalifikatorów po prostu do typu, co powoduje, że VS tworzy tymczasowy, w którym inne deklarują zmienną. Gdzie może być jeszcze gorzejB b(A::A(i));
jest analizowany przez clang i gcc jako najbardziej irytujący parsowanie, ale VS widzi to jako deklarację zmiennejb
typuB
z inicjatorem. Czy nadal jest wiele różnic, które są poważne?Oczywiste jest, że w przenośnym kodzie należy unikać zbędnych kwalifikatorów. Czy istnieje dobry sposób, aby zapobiec użyciu tego konstruktu?