Почему я могу использовать авто для частного типа?

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

126

Решение

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

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

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

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

104

Другие решения

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

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
}
102

На этот вопрос уже очень хорошо ответили Чилл и Р. Мартиньо Фернандес.

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

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

Спасибо Квентину за то, что он напомнил мне о лазейке Гарри.

9

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

7