2012-02-26 5 views
6

Следующий код не компилируется в GCC:C++ и впрыскивается базовое имя

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(); 
     } 
    }; 

}; 

И это дает:

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

Теперь, мне сказали, что это правильное поведение (из-за введенного основания имя One :: A делает A ссылкой на One :: A). Однако этот код компилируется в C# (ну, после изменения нескольких вещей), поэтому это похоже на C++.

Что мне интересно, почему? Существует ли конкретная цель для инъекции базового имени «One :: A» как «A»?

+6

Только для справки, C#! = C++. Я не уверен, почему вы сравниваете два –

+0

. Я не могу сказать точно (у меня нет моей копии со мной прямо сейчас), но в стороне, вопросы о том, почему язык C++ был разработан так, как он были часто отвечены в книге под названием «Дизайн и эволюция C++» от Stroustrup. –

+0

@TonyTheLion, ну, это другой c-подобный язык программирования, который имеет «пространство имен», и тот, к которому я имел доступ к компилятору. Я не говорю, что есть только «Один истинный путь», в котором это правильно, но это не показалось интуитивным, по крайней мере, с учетом этого примера. – kamziro

ответ

3

Единственная причина, я могу думать о том, что в C++ вы, вероятно, относятся к имени базового класса в списке инициализации конструктора, например:

namespace Two { 

    /*...*/ 

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

Конечно цель, то разные из того, что в вашем примере, потому что вы фактически объявляете локальную переменную внутри конструктора, тогда как в моем примере A() относится к объекту типа A, который неявна в определении class B из-за наследования.

Однако ситуация с моим примером более вероятна, поэтому, я думаю, они думали, что давайте не будем требовать, чтобы пространство имен было сделано явным в этом случае. Как следствие, любая ссылка на A без пространства имен интерпретируется как ссылка на базовый класс, а не на любой другой класс с именем A, даже если он находится в том же пространстве имен, что и объявление B.

+0

А, да, это имеет смысл на самом деле. Хотя я и говорю, что он немного похож на «хак». – kamziro

3

Есть ли конкретная цель для инъекции базового имени «Один :: А» как «А»?

Да. Это так, что вы могли бы написать это:

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

В отсутствие закачанного-имя, вы, чтобы написать N::A *a, не приятно.

Обратите внимание, что из-за введенного-имени, следующие строки допускаются:

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

Разве это не отличается от ситуации, объясненной айером?В исходном примере 'class B' находился внутри одного пространства имен, но класс, из которого он был получен, был в другом. – jogojapan

+1

@jogojapan Но 'class B' наследует все имена из своего базового класса, включая введенное имя самого базового класса. Будучи во внутренней области, он скрывает имена из окружающего пространства имен. –

+0

@Bo Persson Да. Я просто указываю, что приведенный выше пример не совсем иллюстрирует этот момент. В приведенном выше примере единственная ссылка на «класс А» находится в том же пространстве имен, что и объявление самого класса. Для этого вам не нужна инъекция имен. – jogojapan

0

По квалификации A с One:: вы добавили A из namespace один в объеме, так что компилятор будет выглядеть там для разрешения имени.

+0

Но объект типа 'A', объявленный внутри конструктора, не был квалифицирован с помощью« One :: '. Он интерпретируется как принадлежащий 'namespace One', хотя декларация самого класса B фактически находится внутри' namespace Two'. – jogojapan