Bill Rubin said:
Thanks very much for your helpful response. However, I'm still uncertain
about your explanation.
Sorry I was away from the newsgroup for a while and wasn't able to
respond in a timely fashion. Carl's answers are correct; I'll just
take a slightly different route to the same result.
wouldn't be able to use those either.
I tried that in VC++ 7.1, and you're half right: An enum causes the same
error, as you predict. But a typedef is accepted just fine. Is this a
compiler bug? At best, the behavior seems inconsistent.
It's a bug.
I'm confused about which principle should govern. Paraphrasing Stroustrup, Section 15.3:
1. The name of a public member of a class can be used by any function.
2. Access control is applied uniformly to names. What a name refers to
does not affect the control of its use.
Principle 2 is essentially what you're saying. If this governs, then it
seems to me that Principle 1 -- the first principle of access control --
must be qualified to say something like " ... unless the function is in a
distantly related scope ...", a rather unwieldy principle! Am I missing
something here? On the other hand, consider Stroustrup, Section 5.3.2:
Yes. The thing you're missing is that _how_ the name is found is
also important. (Let me preface this by saying that what I'm
describing is how the language is defined, not what is currently
implemented in the 7.1 compiler. VC++ 7.1 is much closer to
Standard C++ than 6.0 was, but it's still not quite there yet.
You can try these out using
www.comeaucomputing.com/tryitout and
get the right answer, though.)
The key factor in all of this, as I mentioned in my earlier post,
is the following citation from the C++ Standard (11.2 para 1):
If a class is declared to be a base class (clause 10) for
another class using the public access specifier, the public
members of the base class are accessible as public members of
the derived class and protected members of the base class are
accessible as protected members of the derived class. If a
class is declared to be a base class for another class using
the protected access specifier, the public and protected
members of the base class are accessible as protected members
of the derived class. If a class is declared to be a base
class for another class using the private access specifier, the
public and protected members of the base class are accessible
as private members of the derived class. [Footnote: As
specified previously in clause 11, private members of a base
class remain inaccessible even to derived classes unless
friend declarations within the base class declaration are used
to grant access explicitly.]
There are three different ways you can name A::function when you
call it from DistantlyRelated::g(), and each has different effects
with respect to accessibility:
1) If you use no qualification (just "function()"), the name
"function" is a public member of A and thus a private member of
CloselyRelated, and thus not accessible (as described in the
footnote quoted above) from DistantlyRelated.
2) Now consider the case you gave, "A::function()". There are
two rules that affect this analysis. First, the access of a
name is considered relative to its "naming class" (i.e., the
class in which the name is looked up). In this case, that's
"A" -- "A::function" means "look up 'function' in class 'A'".
"function" is public when named in "A", so that part works out
fine.
The second rule has to do with something called "class name
injection" -- the name of a class is "injected" into the class for
lookup purposes. (To keep this relatively short, I won't discuss
the reasons for this here, but if you're really curious, ask and
I'll go into it.) This means that when you say "A" in
DistantlyRelated, you're really finding the "A" that's a member of
the "A" class -- and _it's_ inaccessible, for the same reason an
unqualified reference to "function" is. The upshot is that, even
though "function" is public when named in "A", the reference
"A::function" still fails the access check because "A" isn't
accessible.
3) The third way to write this reference is "::A::function" --
and that works. The reason is that it doesn't fall afoul of
either of the two preceding problems: "::A" is accessible, because
"::A" refers to the name of the class in global scope, where no
access applies, rather than the injected name that's a member of
the class "A", and (as I mentioned) "function" is public when
named in "A".
Unfortunately, this won't help you with your program, because
VC++ 7.1 either hasn't implemented class name injection or hasn't
gotten it right, I'm not sure which; "::A::function" fails the same
way that "A::function" does. (Most compilers that haven't yet
implemented class name injection fail by accepting _both_
"A::function" [incorrectly] and "::A::function", which is why I'm
not quite sure about VC++ 7.1.) However, I thought it might be
helpful to explain the theory behind what _should_ be happening and
then relate it to what you're actually seeing.
3. The access specifier for a base class controls ... the conversion of
pointers and references from the derived class type to the base class type.
Only friends and members of a derived class can convert a derived class
ptr/ref to a ptr/ref of a private base class.
My understanding has been that Principle 3 is the essence of access to
private base class members. Applying Principle 3 to my example, non-static
member functions and data members of "A" would be inacessible to
DistantlyRelated, not because they "essentially ... become private members
of CloselyRelated" (a transformation I haven't been able to find
justification for), but because DistantlyRelated cannot convert its "this"
pointer to an "A*".
Actually, your #3 is parallel to the access control of names. In
order to call a nonstatic member function, the name must be accessible
_and_ the object or pointer you use must be convertible to the class
of the member function. The call is invalid if either of these
conditions fails.
-- William M. Miller
The MathWorks, Inc.