Меня как-то удивило, что следующий код компилируется и запускается (vc2012 & gcc4.7.2)
class Foo {
struct Bar { int i; };
public:
Bar Baz() { return Bar(); }
};
int main() {
Foo f;
// Foo::Bar b = f.Baz(); // error
auto b = f.Baz(); // ok
std::cout << b.i;
}
Верно ли, что этот код компилируется нормально? И почему это правильно? Почему я могу использовать auto
частный тип, в то время как я не могу использовать его имя (как и ожидалось)?
c++
c++11
auto
private-members
Хансмаад
источник
источник
f.Baz().i
это тоже нормально, как естьstd::cout << typeid(f.Baz()).name()
. Код за пределами класса может «видеть» тип, возвращаемый,Baz()
если вы можете получить его, вы просто не можете назвать его.private
существует удобство описания API таким образом, чтобы компилятор мог помочь в обеспечении соблюдения. Он не предназначен для предотвращения доступа к типу соBar
стороны пользователейFoo
, так что это не мешаетFoo
каким - либо образом предлагать , что доступ возвращая экземплярBar
.#include <iostream>
. ;-)Ответы:
Правила для
auto
большей части такие же, как и для вывода типа шаблона. Опубликованный пример работает по той же причине, по которой вы можете передавать объекты частных типов в функции шаблона:template <typename T> void fun(T t) {} int main() { Foo f; fun(f.Baz()); // ok }
Вы спросите, почему мы можем передавать объекты частных типов шаблонным функциям? Потому что недоступно только название типа. Сам тип по-прежнему можно использовать, поэтому вы вообще можете вернуть его в клиентский код.
источник
public: typedef Bar return_type_from_Baz;
классFoo
в вопрос. Теперь тип можно идентифицировать по общедоступному имени, несмотря на то, что он определен в закрытом разделе класса.private: typedef Bar return_type_from_Baz;
кFoo
, как показано .typedef
Идентификаторы 'd не обращают внимания на спецификаторы доступа, публичные и частные.Bar
илиSomeDeducedType
? Не то чтобы я мог использовать его для доступа к закрытым членамclass Foo
или чему-то еще.К именам применяется контроль доступа . Сравните с этим примером из стандарта:
class A { class B { }; public: typedef B BB; }; void f() { A::BB x; // OK, typedef name A::BB is public A::B y; // access error, A::B is private }
источник
На этот вопрос уже очень хорошо ответили и Чилл, и Р. Мартинью Фернандес.
Я просто не мог упустить возможность ответить на вопрос аналогией с Гарри Поттером:
class Wizard { private: class LordVoldemort { void avada_kedavra() { // scary stuff } }; public: using HeWhoMustNotBeNamed = LordVoldemort; friend class Harry; }; class Harry : Wizard { public: Wizard::LordVoldemort; }; int main() { Wizard::HeWhoMustNotBeNamed tom; // OK // Wizard::LordVoldemort not_allowed; // Not OK Harry::LordVoldemort im_not_scared; // OK return 0; }
https://ideone.com/I5q7gw
Спасибо Квентину за то, что напомнил мне о лазейке Гарри.
источник
friend class Harry;
пропавшего?friend class Dumbledore;
Wizard::LordVoldemort;
современного C ++. Вместо этого он звонитusing Wizard::LordVoldemort;
. (Честно говоря, использование Волан-де-Морта кажется не таким естественным. ;-)Для того, чтобы добавить другие (хорошие) ответы, вот пример из C ++ 98 , который показывает , что проблема на самом деле не нужно делать с
auto
вообщеclass Foo { struct Bar { int i; }; public: Bar Baz() { return Bar(); } void Qaz(Bar) {} }; int main() { Foo f; f.Qaz(f.Baz()); // Ok // Foo::Bar x = f.Baz(); // f.Qaz(x); // Error: error: ‘struct Foo::Bar’ is private }
Использование частного типа не запрещено, это было только наименование типа. Создание безымянного временного объекта этого типа нормально, например, во всех версиях C ++.
источник