By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
424,680 Members | 2,139 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 424,680 IT Pros & Developers. It's quick & easy.

Error C2247 for distantly-related static member function

P: n/a
The following code snippet shows that VC++ 7.1 correctly compiles a static member function invocation from an Unrelated class, since this static member function is public. I expected to compile the same invocation from a DistantlyRelated class. What actually happened was that the compiler produced:

error C2247: 'A::function' not accessible because 'CloselyRelated' uses 'private' to inherit from 'A'

I'm guessing that the above compiler error message may be caused by too-aggressive "compiler conformance work". If A::function() were not static, a compiler error would be appropriate, because the compiler would need to supply an implicit object reference. But no object reference is needed, since function() is static.

struct A {
static void function() {}
};

class Unrelated {
void f() {A::function();} // Compiles fine, as expected.
};

class CloselyRelated : private A {
void f() {A::function();}
};

class DistantlyRelated : public CloselyRelated {
void g() {A::function();} // Unexpectedly causes error C2247
};

Is this in fact an compiler bug? If so, is it already known to Microsoft?

Bill Rubin
Nov 17 '05 #1
Share this Question
Share on Google+
6 Replies


P: n/a
"Bill Rubin" <an*******@discussions.microsoft.com> wrote in message
news:2F**********************************@microsof t.com...
The following code snippet shows that VC++ 7.1 correctly compiles a static member function invocation from an Unrelated class, since this static member
function is public. I expected to compile the same invocation from a
DistantlyRelated class. What actually happened was that the compiler
produced:
error C2247: 'A::function' not accessible because 'CloselyRelated' uses 'private' to inherit from 'A'
I'm guessing that the above compiler error message may be caused by too-aggressive "compiler conformance work". If A::function() were not
static, a compiler error would be appropriate, because the compiler would
need to supply an implicit object reference. But no object reference is
needed, since function() is static.
struct A {
static void function() {}
};

class Unrelated {
void f() {A::function();} // Compiles fine, as expected.
};

class CloselyRelated : private A {
void f() {A::function();}
};

class DistantlyRelated : public CloselyRelated {
void g() {A::function();} // Unexpectedly causes error C2247
};

Is this in fact an compiler bug? If so, is it already known to Microsoft?


This is not a compiler bug. It's the way the language is
designed to work. When you use private inheritance, you're
saying that the private base class is part of the
implementation -- not part of the interface -- of the
derived class. Essentially, it means that the public members
of the base class become private members of the derived class.

In other words, the effect in your example is like

class CloselyRelated {
private:
static void function();
};

class DistantlyRelated : public CloselyRelated {
void g() { function(); // error: private
}
};

This has nothing to do with the fact that "function" is static.
Access is primarily based on names. If "A" defined a typedef or
an enumeration constant, "DistantlyRelated" wouldn't be able to
use those, either. You can't use anything in "A" because
"CloselyRelated" has effectively closed the gate by using
private inheritance.

-- William M. Miller
The MathWorks, Inc.
Nov 17 '05 #2

P: n/a
Thanks very much for your helpful response. However, I'm still uncertain about your explanation.
If "A" defined a typedef or an enumeration constant, "DistantlyRelated" 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

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

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*"

As I see it, Principle 2 does not apply to inheritance because inheritance-related access restrictions are not governed by names, but by "this" pointer up-conversions. If my understanding is correct, we can now apply an unmodified Principle 1 to my example, showing that public members of "A" other than non-static member functions and data members (typedefs, enums, static members, embedded classes) should be accessible in DistantlyRelated, as I'd first thought

Bill Rubin
Nov 17 '05 #3

P: n/a
I'd be most interested if someone could post to this thread to help resolve the conflict between the two posts (Miller's and mine) of 2/5. Is Miller's analysis (post on 2/5) correct? If so

Q1: Does that mean that the rule "The name of a public member of a class can be used by any function" is not always correct

Q2: What is the flaw in my analysis

Q3: Is VC++ 7.1's acceptance of a distantly-related typedef a compiler bug

Bill Rubin
Nov 17 '05 #4

P: n/a
Bill Rubin wrote:
Thanks very much for your helpful response. However, I'm still
uncertain about your explanation.
If "A" defined a typedef or an enumeration constant,
"DistantlyRelated" 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.


That's a bug - the typedef shouldn't be accessible.

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:
You're missing something.

In C++ there are a couple of things that interact to determine how a name is
resolved when it's referenced inside a class member.

1. Names defined in derived classes hide definitions of the same name in the
base class.
2. Access control is applied after name lookup in all cases.

So in your original example, since 'function' is not (re-)defined in
CloselyRelated, it's definition from A is visible in DistantlyRelated.
Since CloselyRelated privately inherits from A, the names in A are
inaccessible in DistantlyRelated. Note that visibility and accessibility
are separate concerns and do not interact.

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.
This is in effect another way of stating the same thing when applied to
access of non-static, non-type members of a class from within a member
function of a derived class. It also applies outside the class: A client
cannot assign a CloselyRelated* value to an A* variable due to the private
inheritance.

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*".
That would be true if this pointer conversion was the only thing involved -
it's not. Private inheritance means that the names are not accessible. It
also means that the corresponding this pointer conversion is illegal.

As I see it, Principle 2 does not apply to inheritance because
inheritance-related access restrictions are not governed by names,
but by "this" pointer up-conversions. If my understanding is
correct, we can now apply an unmodified Principle 1 to my example,
showing that public members of "A" other than non-static member
functions and data members (typedefs, enums, static members, embedded
classes) should be accessible in DistantlyRelated, as I'd first
thought.


HTH

-cd
Nov 17 '05 #5

P: n/a
Thanks very much, Carl, for helping to explain the situation and confirm Miller's post. (Your post must have crossed with mine earlier today.)

So public members of a class "A" will be hidden from a DistantlyRelated class without regard to whether an object reference is needed to use them. I'm surprised!

In my code, I worked around my compilation problem using the following ugly hack: As a supplementary part of A's effective interface, I define a global forwarding function

inline void functionInA() {A::function();}

Then, in place o

void DistantlyRelated::g() {A::function();} // error -- does not compil

I writ

void DistantlyRelated::g() {functionInA();}

which accomplishes what I intended. To summarize, one can invoke "A::function()" in unrelated and closely related code, but in distantly related code one must use the hack "functionInA()".

I think I'd feel better about this if I understood why this aspect of the C++ language makes more sense than my (apparently wrong) analysis

Bill Rubin
Nov 17 '05 #6

P: n/a
"Bill Rubin" <an*******@discussions.microsoft.com> wrote in message
news:1D**********************************@microsof t.com...
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.
If "A" defined a typedef or an enumeration constant, "DistantlyRelated"

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.
Nov 17 '05 #7

This discussion thread is closed

Replies have been disabled for this discussion.