Подтвердить что ты не робот

Делегирование в частные части

Иногда С++ понятие конфиденциальности просто меня озадачивает: -)

class Foo
{
    struct Bar;
    Bar* p;

public:

    Bar* operator->() const
    {
        return p;
    }
};

struct Foo::Bar
{
    void baz()
    {
        std::cout << "inside baz\n";
    }
};

int main()
{
    Foo::Bar b;   // error: 'struct Foo::Bar' is private within this context

    Foo f;
    f->baz();     // fine
}

Так как Foo::Bar есть private, я не могу объявить b в main. Тем не менее я могу просто называть методы из Foo::Bar. Почему, черт возьми, это разрешено? Это был несчастный случай или по дизайну?


Ой, подождите, становится лучше:

Foo f;
auto x = f.operator->();   // :-)
x->baz();

Даже если мне не позволено называть тип Foo::Bar, он отлично работает с auto...


Ной писал (а):

имена типов, определенные в определении класса, не могут использоваться вне их класса без квалификации.

Просто для удовольствия, вот как вы можете получить тип снаружи:

#include <type_traits>

const Foo some_foo();

typedef typename std::remove_pointer<decltype( some_foo().operator->() )>::type Foo_Bar;
4b9b3361

Ответ 1

Попытка найти что-нибудь в стандарте, которое подробно изложит его, но я не могу. Единственное, что я могу найти, это 9.9:

Имена типов подчиняются точно тем же правилам области, что и другие имена. В частности, имена типов, определенные в определении класса, не могут использоваться вне их класса без квалификации.

По сути, имя Foo:: Bar является приватным для Foo, а не для определения. Таким образом, вы можете использовать Bars вне Foo, вы просто не можете ссылаться на них по типу, поскольку это имя является закрытым.

Правила поиска имен для членов также, похоже, могут повлиять на это. Я не вижу ничего, что конкретно ссылается на "вложенный класс", и, следовательно, им не разрешат (если мое отсутствие чего-то на самом деле происходит потому, что оно не существует).

Ответ 2

Я не могу дать полный ответ, но, возможно, отправную точку. Спецификация С++ 1998 включает следующий пример кода в абзаце 11.3 [class.access] (стр. 175):

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
}

В этом примере частный тип "публикуется" через общедоступный typedef. Хотя это не то же самое, что публикация типа через подпись функции-члена, это похоже.

Ответ 3

Я думаю, что это по дизайну. Вы не можете явно создать экземпляр Foo::Bar, но он может быть возвращен из функций-членов, а затем вы можете передать его другим функциям-членам. Это позволяет скрыть детали реализации вашего класса.