I am reading this Stack Overflow question, and I added a constructor to the code from that question as the follwing,
class Foo {
struct Bar {
[Some standardese ahead]
Let's agree that auto
deduction works in the same way as template argument deduction:
[dcl.spec.auto]/p7
If the placeholder is the auto type-specifier, the deduced type is determined using the rules for template argument deduction
Templates are subject to the two-phase lookup during compilation. Access control is applied to name lookup in the first phase
[basic.lookup]/p1
Overload resolution (13.3) takes place after name lookup has succeeded. The access rules (Clause 11) are considered only once name lookup and function overload resolution (if applicable) have succeeded. Only after name lookup, function overload resolution (if applicable) and access checking have succeeded are the attributes introduced by the name’s declaration used further in expression processing
auto
and decltype(auto)
are usually a placeholder for a deduced type, and it is true that [temp.arg]/p3 says
The name of a template-argument shall be accessible at the point where it is used as a template-argument
but names aren't involved here, only the types. Access control applies to names, a type can be mapped to 0, 1 or multiple names and that's what you're dealing with when using auto
in the code above: it is semantically equivalent to the semantics of template deduction and this is by design.
[class.access]/p4
Access control is applied uniformly to all names, whether the names are referred to from declarations or expressions. [...] The accessibility of the entity referred to by the typedef is not considered. For example
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
}
To convince yourself of the following take a look at the same code with template argument deduction involved (conceptually equivalent to the auto
version)
template
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 <
Live example
In the code above names aren't involved and thus access control doesn't apply. Types are indeed involved.
The semantics of auto
is like implicit template deduction (the normative wording also directly refers to it).
Someone else had this doubt before.
Now for the answers:
Case 1
is easy to agree with if you consider that the caller has no access to the name Foo::Bar
.
Case 2
exposes the name to the caller as well, so if you use the typedef'd name your code will happily compile.