inner class accessing outer

M

Matthias Buelow

Hi folks,

I've got something like:

class Outer {
int f();

friend class Inner;
class Inner {
int g() {
// call f from here
}
}
}


and I want to call Outer::f from inside g. Simply doing Outer::f()
doesn't work, gcc complains it doesn't have an object pointer (which is
somehow bogus, imho -- the compiler could derive a pointer to Outer
because Inner is instantiated within Outer).

Or could I perhaps obtain Outer's this from Inner?

I'm aware of the workaround that involves passing a pointer to the Outer
instance to Inner's constructor. That's what I'd like to avoid.

Thanks.
 
V

Victor Bazarov

Matthias said:
I've got something like:

class Outer {
int f();

friend class Inner;

I am not sure this is necessary...
class Inner {
int g() {
// call f from here
}
}
}

I believe a few semicolons are missing here...
and I want to call Outer::f from inside g.

On what 'Outer' object? Do you have an 'Outer' object to call 'f'
for? 'f' is non-static, so you need an object of class 'Outer' which
to supply to 'f' as the hidden argument.
Simply doing Outer::f()
doesn't work, gcc complains it doesn't have an object pointer
Correct.

(which
is somehow bogus, imho -- the compiler could derive a pointer to Outer
because Inner is instantiated within Outer).

Who is instantiated inside of whom? 'Inner' _class_ is *declared*
insde the 'Outer' _class_. There is no relationship between them
from the instances POV. C++ is not Java.
Or could I perhaps obtain Outer's this from Inner?

If your 'Inner' keeps a {pointer to|reference to|instance of} Outer,
you can of course "obtain" it from 'this'. Otherwise, you need to
pass the 'Outer' you need as the argument.
I'm aware of the workaround that involves passing a pointer to the
Outer instance to Inner's constructor. That's what I'd like to avoid.

Don't pass it to the constructor. Pass it to 'g'.

V
 
M

Matthias Buelow

Victor said:
I believe a few semicolons are missing here...
Yes...

Who is instantiated inside of whom? 'Inner' _class_ is *declared*
insde the 'Outer' _class_. There is no relationship between them
from the instances POV. C++ is not Java.

Errm. I posted a bit carelessly. Suppose that:

class Outer {
int f() {}
class Inner {
int g() { /* want to call f here */ }
} inner;
};

Now Outer::Inner::g() could possibly have access to the Outer object, if
the compiler does the Right Thing... but apparently it's not supported
or is it somehow, that's what I want to know.
 
V

Victor Bazarov

Matthias said:
Errm. I posted a bit carelessly. Suppose that:

class Outer {
int f() {}
class Inner {
int g() { /* want to call f here */ }
} inner;
};

Now Outer::Inner::g() could possibly have access to the Outer object,
if the compiler does the Right Thing... but apparently it's not
supported or is it somehow, that's what I want to know.

OK... It is not documented (at least not in that particular sense), so
you cannot rely on it, but if 'inner' is the only member in 'Outer', it
is most likely that the address of 'inner' is the same as the address
of the 'Outer' object that contains it. You can use two 'static_cast'
to convert to 'char*' and to 'Outer' from 'this'. But don't tell anyone
I told you.

Relying on that behaviour is a VERY BAD IDEA(tm) because somebody can
put another member of some type before 'inner' in the 'Outer' class,
and the assumption goes down the drain. That's why I don't think there
exists any reliable way of getting the object containing 'Inner::this'
from it based solely on the fact that it does contain it. What if I
write

class SomeOtherOuter {
double a;
char *b;
Outer::Inner myowninner;
};

and call 'g' for 'myowninner'? Yeah, yeah, you can claim that 'Inner'
is private in 'Outer' (what if it isn't?) but I can then say that I
edit the 'Outer' to make 'SomeOtherOuter' a friend... See how easy it
is to bring down that house of cards?

The only reliable way is to let 'Inner' instance know in what 'Outer'
it is contained. And you already know how to do that. Another way
is _not_ to let 'Inner' know, but pass 'Outer' as the argument to
the 'g()'.

V
 
M

Matthias Buelow

Victor said:
but if 'inner' is the only member in 'Outer', it

Yeah, except that it isn't, in my real code.
class SomeOtherOuter {
double a;
char *b;
Outer::Inner myowninner;
};

and call 'g' for 'myowninner'? Yeah, yeah, you can claim that 'Inner'
is private in 'Outer' (what if it isn't?) but I can then say that I
edit the 'Outer' to make 'SomeOtherOuter' a friend... See how easy it
is to bring down that house of cards?

Hmm.. this is indeed a counter-example. However, if an inner class
hypothetically accessed parts of its outer class "directly", the
compiler could flag an error if the inner class would be used anywhere
outside of the outer class it is defined in (or automatically make it
"private" or somesuch).
The only reliable way is to let 'Inner' instance know in what 'Outer'
it is contained. And you already know how to do that. Another way
is _not_ to let 'Inner' know, but pass 'Outer' as the argument to
the 'g()'.

Yeah.. but it's more typing (and hence uglier code..)
 
A

Alf P. Steinbach

* Matthias Buelow:
I've got something like:

Sure. The error is on line 42 of the actual code. By the way, I've got
/something like/ the Boston Bridge to sell you, are you interested?

Cheers, & hth,.

- Alf
 
V

Victor Bazarov

Matthias said:
Yeah, except that it isn't, in my real code.


Hmm.. this is indeed a counter-example. However, if an inner class
hypothetically accessed parts of its outer class "directly"

There is no "its outer class". I am guessing you don't get the point
that 'Inner's being declared inside 'Outer' is inconsequential to the
nature of 'Inner'-'Outer' relationship as far as C++ is concerned.

The only important thing here is that 'Outer::Inner' is a separate
type from any other 'Inner'.
, the
compiler could flag an error if the inner class would be used anywhere
outside of the outer class it is defined in (or automatically make it
"private" or somesuch).

That's nonsense. It would limit the use of 'Inner' to inside of the
"outer" class, which is unacceptable; besides, nothing is automatic
in the matters of access specifiers beyond simple "everything is
private by default in 'class' and public in 'struct'".

To delcare 'Inner' inside 'Outer' like you did is *not* to prohibit
the use of 'Inner' outside of 'Outer', but to define a *conceptual*
relationship between the types, which then realised during name
lookup -- if you use the name "Inner" in the 'Outer' scope, it will
first of all resolve to 'Outer::Inner', and not any other 'Inner',
that's essentially all.

Yes, private/protected mechanisms can be used to prevent access to
'Outer::Inner' from other scopes, but that's another story (and it
comes _after_ name lookup anyway). And 'Inner' cannot know whether
it's declared "private" or not inside the 'Outer'. Heck, 'Inner'
has really no idea that it's declared inside some other scope at
all (not that it's disallowed to gain access to that information,
it just doesn't have any need in most cases).

You might want to review your design: why do you need 'Inner' to
be inner to the 'Outer'? What is the problem such relationship
is solving? What is the relationship between entities in the
problem domain that you are modeling?

V
 
M

Matthias Buelow

Victor said:
There is no "its outer class". I am guessing you don't get the point
that 'Inner's being declared inside 'Outer' is inconsequential to the
nature of 'Inner'-'Outer' relationship as far as C++ is concerned.

That's not entirely true -- consider the following example:

class Outer {
typedef int T;
public:
class Inner {
T x;
};
};

this could be extended to object relationship (not just types).
That's nonsense. It would limit the use of 'Inner' to inside of the
"outer" class, which is unacceptable;

In this case it would be the behaviour I want; certainly far from
"nonsense".
And 'Inner' cannot know whether
it's declared "private" or not inside the 'Outer'. Heck, 'Inner'
has really no idea that it's declared inside some other scope at
all (not that it's disallowed to gain access to that information,
it just doesn't have any need in most cases).

"Inner" doesn't; but the compiler does.
 
T

Tadeusz B. Kopec

Hi folks,

I've got something like:

class Outer {
int f();

friend class Inner;
class Inner {
int g() {
// call f from here
}
}
}


and I want to call Outer::f from inside g. Simply doing Outer::f()
doesn't work, gcc complains it doesn't have an object pointer (which is
somehow bogus, imho -- the compiler could derive a pointer to Outer
because Inner is instantiated within Outer).

Or could I perhaps obtain Outer's this from Inner?

I'm aware of the workaround that involves passing a pointer to the Outer
instance to Inner's constructor. That's what I'd like to avoid.

Thanks.

First of all - you mistake C++ and Java. C++ inner classes are like
static inner classes in Java - no owning object of outer class, so no
pointer to it. You have to give an Outer object to g somehow (a parameter
to g or a parameter to Inner constructor).
Second:
friend class Inner;
is a way to let functions of class Inner access private members of class
Outer (good idea as f seems to be private). But you must put this line
after definition of class Inner. Otherwise the compiler will assume you
are referring to a class defined somewhere outside the class Outer not
the class you intended.

HTH
 
V

Victor Bazarov

Matthias said:
That's not entirely true -- consider the following example:

class Outer {
typedef int T;
public:
class Inner {
T x;
};
};

this could be extended to object relationship (not just types).

I wonder how. Instances don't have scopes. Members don't exist
without instances. Here, you have 'T' which is a name that can be
looked up at the compilation time, and the scope of 'Outer::Inner'
has a very specific set of rules for name lookup.

Now, remove they 'typedef' (to make 'T' an instance). What do you
have?

class Outer {
int T;
public:
class Inner {
void foo() { T = 42; }
};
};

You have 'T' that is an instance of 'int', but it doesn't exist
in a vacuum. It only exists in an instance of 'Outer'. The
compiler has no idea where that instance of 'Outer' is. No clue
whatsoever. What do you propose? Let's rehearse the conversation
you're going to have in 'comp.std.c++' (see my suggestion below).

You're saying that if you declare an instance of 'inner' inside
an instance of 'Outer', the compiler has to know how to get the
address of one and convert it to the address of the other. How?
What if it doesn't exist?

class Outer {
int T;

class Inner {
void foo() { T = 42; }
} inner;

void bar();
};

void Outer::bar() {
Inner i;
i.foo(); // now what?! Whose 'T' are we changing now?
}

Are you going to prohibit the use of 'Inner' like that? Fine,
somehow 'Inner' is private (as I made it in my last example), but
it should also become incomplete class (so you cannot instantiate
it as a stand-alone object even inside 'Outer' scope)? Then how
do we instantiate the 'Outer::inner' member? See the conflict?
Or do we simply allow 'i' inside 'Outer::bar' to change 'this->T'
somehow?

Think about all possible variations of the use of 'Inner' and not
just how _you_ want to use it. I believe the expression is "to
see beyond one's nose".
In this case it would be the behaviour I want; certainly far from
"nonsense".

But you seem to want to have this behaviour as the norm. It's not
because it cannot be. It just wouldn't make sense as the norm.

It's impossible to make everybody happy, that's true. That's why
the langauges are usually designed to make the majority of people
happy and if a few unhappy people remain, they usually get to use
the work-arounds.
"Inner" doesn't; but the compiler does.

So? The compiler knows many other things. Every compiler knows
its own set of things, AAMOF. If you need to be able to know the
things the compiler knows, turn to the compiler's manual. If you
want to make what compiler knows available to the programmer and
specify that in the Standard, then it's a different story.

I believe you're arguing a change in the language. I don't think
this is the right newsgroup for it. Try 'comp.std.c++'. Explain
what you think is missing from the language, make a proposal. Do
not forget to have a business case (what problem it solves that
cannot be solved otherwise). Then present your argument and have
the language changed. Or have your mind changed for the work-
around that you're "trying to avoid". Either way, 'comp.std.c++'
is _the_ newsgroup for trying to get your point across to the
people who are on the bleeding edge of making C++ work better.

V
 
M

Matthias Buelow

Victor said:
I wonder how. Instances don't have scopes. Members don't exist
without instances. Here, you have 'T' which is a name that can be

Well.. one could use lexical scope. This would break the homology with
C, where struct X { struct Y {...}; }; makes struct Y visible within the
whole compilation unit but well.
class Outer {
int T;

class Inner {
void foo() { T = 42; }
} inner;

void bar();
};

void Outer::bar() {
Inner i;
i.foo(); // now what?! Whose 'T' are we changing now?
}
Or do we simply allow 'i' inside 'Outer::bar' to change 'this->T'
somehow?

Well, I see the problem. Changing it to mean "this->T" in this case
would be the most obvious solution, imho. Actually, I think that would
be quite neat but perhaps not fit entirely into the C++ mindframe...
Think about all possible variations of the use of 'Inner' and not
just how _you_ want to use it. I believe the expression is "to
see beyond one's nose".

This could be made even more powerful, like treating T (in this example)
somewhat akin to a free variable and binding it to the (lexically)
surrounding binding, for example:

void Outer::bar() {
int T;
Inner i;
i.foo(); // would refer to T in bar()'s scope
}

This could be resolved at compile-time.

Of course, the next problem would be:

void Outer::bar() {
int T;
Inner *i = new Inner;
i->foo(); // now what?
}

I don't think it's a real show-stopper but I'm not into going into this
now.
I believe you're arguing a change in the language. I don't think
this is the right newsgroup for it. Try 'comp.std.c++'. Explain

I don't really want to; I just didn't know if there wasn't perhaps some
way to have an "inner" class access members of a surrounding class.
 
J

James Kanze

I am not sure this is necessary...

It depends on the version of the standard his compiler
implements. It's probably a good idea, but it almost definitly
should go *after* the definition (or at least a declaration) of
Inner; if it's before, as here, it says that there is some class
named Inner in the enclosing namespace which is a friend.
 
B

Barry

Matthias said:
Hi folks,

I've got something like:

class Outer {
int f();

friend class Inner;
class Inner {
int g() {
// call f from here
}
}
}


and I want to call Outer::f from inside g. Simply doing Outer::f()
doesn't work, gcc complains it doesn't have an object pointer (which is
somehow bogus, imho -- the compiler could derive a pointer to Outer
because Inner is instantiated within Outer).

Or could I perhaps obtain Outer's this from Inner?

I'm aware of the workaround that involves passing a pointer to the Outer
instance to Inner's constructor. That's what I'd like to avoid.

According to the current standard, it's necessary to declaration nested
class friend.
IIRC, VC does NOT need the friend declaration.

Anyway, your way of declaring nested class a friend is wrong.

class Outter
{
class Inner
{
};
friend class Inner;
};

or

class Outter
{
class Inner; // needed, or else,
friend class Inner; // Inner is a friend from the enclosing namespace

class Inner
{
};
};
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,954
Messages
2,570,116
Members
46,704
Latest member
BernadineF

Latest Threads

Top