2012-02-26 11 views
6

Poniższy kod nie kompilacji w gcc:C++ i wstrzykuje nazwa bazowa

namespace One{ 
    class A{ 
    }; 
}; 

namespace Two{ 
    class A{ 
     public: 
     void what(){ 
      cout << "Two::A says what!" << endl; 
     } 
    }; 

    class B : public One::A{ 
     public: 
     B(){ 
      A xx; 
      xx.what(); 
     } 
    }; 

}; 

I daje:

gccbug.cpp: In constructor ‘Two::B::B()’: 
gccbug.cpp:23: error: ‘class One::A’ has no member named ‘what’ 

Teraz, powiedziano mi, że jest to poprawne zachowanie (na skutek wtryskiwanego podstawy imię One :: A w odniesieniu do One :: A). Jednak ten kod kompiluje się w języku C# (no, po zmianie kilku rzeczy), więc wydaje się, że jest to C++.

Zastanawiam się, dlaczego ... dlaczego? Czy istnieje konkretny cel wstrzykiwania nazwy bazowej "One :: A" jako "A"?

+6

Dla odniesienia, C#! = C++. Nie jestem pewien, dlaczego porównujesz te dwa numery: –

+0

Nie mogę powiedzieć na pewno (nie mam teraz mojego egzemplarza ze mną), ale na bok, pytania o to, dlaczego język C++ został zaprojektowany w taki sposób, często odpowiadano w książce "Projekt i ewolucja C++" autorstwa Stroustrupa. –

+0

@TonyTheLion, No cóż, jest to inny język programowania w stylu c, który ma "przestrzeń nazw" i taki, do którego miałem dostęp do kompilatora. Nie mówię, że jest tylko "Jedna Prawdziwa Droga", w której to jest poprawne, ale to nie wydawało się intuicyjne, przynajmniej podane w tym przykładzie. – kamziro

Odpowiedz

3

Jedynym powodem mogę myśleć, że w C++ jest prawdopodobne, aby odnieść się do nazwy klasy bazowej w liście inicjatora konstruktora, tak:

namespace Two { 

    /*...*/ 

    class B : public One::A { 
    public: 
    B():A() 
    { 
     /*...*/ 
    } 
    }; 
} 

Oczywiście celem wtedy jest inna od tego w twoim przykładzie, ponieważ faktycznie deklarujesz lokalną zmienną wewnątrz konstruktora, podczas gdy w moim przykładzie A() odnosi się do obiektu typu A, który jest ukryty w definicji class B z powodu dziedziczenia.

Jednak sytuacja mojego przykładu jest bardziej prawdopodobna, więc domyślam się, że myśleli, że nie wymagajmy, aby przestrzeń nazw była jawna w tym przypadku. W związku z tym każde odwołanie do A bez przestrzeni nazw jest interpretowane jako odnoszące się do klasy bazowej, a nie do jakiejkolwiek innej klasy o nazwie A, nawet jeśli znajduje się w tej samej przestrzeni nazw co deklaracja B.

+0

Ach, tak, to ma sens. Chociaż mówię, że to trochę przypomina "hack". – kamziro

3

Czy istnieje konkretny cel wstrzykiwania nazwy bazowej "One :: A" jako "A"?

Tak. To jest tak, że można napisać:

namespace N 
{ 
    class A 
    { 
     A *a; 
    }; 
} 

W braku wtryskiwanego-name, masz napisać N::A *a co nie jest miłe.

Zauważ, że to dlatego, że z wtryskiwanego-name, następujące linie są dozwolone: ​​

A::A *a1; //ok 
A::A::A *a2; //ok 
A::A::A::A *a3; //ok 
A::A::A::A::A *a4; //ok 
//and so on 

Online demo

+0

Czy to nie różni się od sytuacji wyjaśnionej przez pytającego?W oryginalnym przykładzie "klasa B" znajdowała się wewnątrz jednej przestrzeni nazw, ale klasa, z której pochodzi, była w innej. – jogojapan

+1

@jogojapan Ale 'klasa B' dziedziczy wszystkie nazwy od swojej klasy bazowej, łącznie z wstrzykniętą nazwą samej klasy bazowej. Będąc w wewnętrznym zasięgu, ukrywa on nazwy z otaczającej przestrzeni nazw. –

+0

@Bo Persson Tak. Po prostu zwracam uwagę, że powyższy przykład nie całkiem ilustruje ten punkt. W powyższym przykładzie jedyne odniesienie do "klasy A" znajduje się w tej samej przestrzeni nazw co deklaracja samej klasy. Nie potrzebujesz do tego imienia. – jogojapan

0

Przez kwalifikacyjna A z One:: dodałeś A z namespace jednym zakresie, więc kompilator będzie tam szukać rozpoznawania nazw.

+0

Ale obiekt typu "A" zadeklarowany wewnątrz konstruktora nie został zakwalifikowany za pomocą 'One ::'. Jest interpretowany jako przynależny do 'namespace One', chociaż sama deklaracja klasy B' znajduje się wewnątrz 'namespace Two'. – jogojapan