делегирование в частные части - PullRequest
12 голосов
/ 01 июня 2010

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

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;

Ответы [ 3 ]

6 голосов
/ 01 июня 2010

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

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

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

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

3 голосов
/ 01 июня 2010

Я не могу дать полный ответ, но, возможно, отправная точка. Спецификация C ++ 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. Хотя это не то же самое, что публикация типа через сигнатуру функции-члена, она похожа.

1 голос
/ 01 июня 2010

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

...