Почему я могу использовать auto в закрытом типе?

Я как-то удивился, что следующий код компилируется и запускается (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 для частного типа, тогда как я не могу использовать его имя (как ожидалось)?

Ответ 1

Правила для auto, по большей части, такие же, как для вывода типа шаблона. Приведенный пример работает по той же причине, что и объекты частных типов для шаблонов:

template <typename T>
void fun(T t) {}

int main() {
    Foo f;
    fun(f.Baz());         // ok
}

И почему мы можем передавать объекты частных типов в функции шаблона, спросите вы? Потому что только имя типа недоступно. Сам тип все еще можно использовать, поэтому вы можете вообще вернуть его к клиентскому коду.

Ответ 2

Контроль доступа применяется к именам. Сравните этот пример со стандартом:

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
}

Ответ 3

Этот вопрос уже очень хорошо ответил как холодом, так и Р. Мартиньо Фернандесом.

Я просто не мог упустить эту возможность ответить на вопрос с аналогией Гарри Поттера:

class Wizard
{
private:
    class LordVoldemort
    {
        void avada_kedavra()
        {
            // scary stuff
        }
    };
public:
    using HeWhoMustNotBeNamed = LordVoldemort;
};

int main()
{
    Wizard::HeWhoMustNotBeNamed tom; // OK
    Wizard::LordVoldemort not_allowed; // Not OK
    return 0;
}

Ответ 4

Чтобы добавить к другим (хорошим) ответам, вот пример из С++ 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
}

Использование частного типа не запрещено, это только имя типа. Создание неназванного временного типа такого типа в порядке, например, во всех версиях С++.