[Некоторые standardese вперед]
Давайте условимся, что auto
вычет работает таким же образом, как шаблон аргумента дедукции:
[dcl.spec.auto]/p7
Если заполнитель является автоматическим спецификатором типа , выведенный тип определяется с использованием th e правила для вывода аргумента шаблона
Шаблоны подпадают под действие two-phase lookup во время компиляции. Контроль доступа применяется к имени поиска в первой фазе
[basic.lookup]/p1
Разрешение перегрузки (13.3) имеет место после имени поиска удалось. Правила доступа (раздел 11) рассматриваются только после успешного поиска имени и разрешения перегрузки функции (если применимо). Только после того, как поиска имен, функция разрешения перегрузки (если это применимо) и проверки доступа преуспел являются атрибуты, введенные декларациями имени в дальнейшем используются при обработке выражения
auto
и decltype(auto)
, как правило, заполнитель для выводимого типа, и правда, [temp.arg]/p3 говорит
имя шаблона-аргумента должен быть доступен в точке, где она используется в качестве шаблона-аргумента
но имена здесь не задействованы, только типы. Контроль доступа применяется к именам, тип может быть сопоставлен с 0, 1 или несколькими именами, и это то, с чем вы имеете дело при использовании auto
в приведенном выше коде: он семантически эквивалентен семантике вычета шаблонов, и это по дизайну.
[класс.доступ]/p4
Контроль доступа применяется единообразно ко всем именам, будь то имена упоминаются из деклараций или выражений. [...] Доступность объекта, упомянутого в typedef, не рассматривается. Например
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
}
Чтобы убедить себя в следующем взглянуть на тот же код с аргументом шаблона дедукцией вовлеченного (концептуально эквивалентно версиями auto
)
template<class T>
T deduce(T t) {
return t;
}
class Foo {
struct Bar{
int i;
Bar(int a = 5):i(a){};
};
public:
Bar *getB() { return new Bar(5); } // Leaks, doesn't matter for clarity's sake
};
int main() {
Foo f;
std::cout <<"b.i="<< deduce(f.getB())->i <<std::endl; // Prints 'b.i=5'
return 0;
}
Live example
В код выше имен не задействован и, следовательно, управление доступом не применяется. Типы действительно задействованы.
Семантика auto
походит на неявный вычет шаблона (нормативная формулировка также прямо относится к нему).
Someone else had this doubt before.
Теперь для ответов:
Case 1
легко согласиться с тем, если вы считаете, что абонент не имеет доступа к имени Foo::Bar
.
Case 2
также предоставляет имя вызывающему, поэтому, если вы используете имя typedef'd, ваш код будет с удовольствием скомпилирован.
Я бы сказал, что нет таких вещей, как частные * типы *. Есть только частные * члены *, и это означает только, что их * имена * недоступны. Сам тип не подлежит контролю доступа. – dyp
dyp is right, имена и типы - это две разные вещи, и они обрабатываются по-разному во время фаз компиляции –