inner class accessing outer

Discussion in 'C++' started by Matthias Buelow, Nov 26, 2007.

  1. 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.
     
    Matthias Buelow, Nov 26, 2007
    #1
    1. Advertisements

  2. I am not sure this is necessary...
    I believe a few semicolons are missing here...
    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.
    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.
    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.
    Don't pass it to the constructor. Pass it to 'g'.

    V
     
    Victor Bazarov, Nov 26, 2007
    #2
    1. Advertisements

  3. 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.
     
    Matthias Buelow, Nov 26, 2007
    #3
  4. 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
     
    Victor Bazarov, Nov 26, 2007
    #4
  5. 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", 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).
    Yeah.. but it's more typing (and hence uglier code..)
     
    Matthias Buelow, Nov 26, 2007
    #5
  6. * Matthias Buelow:
    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
     
    Alf P. Steinbach, Nov 26, 2007
    #6
  7. 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'.
    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
     
    Victor Bazarov, Nov 26, 2007
    #7
  8. 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).
    In this case it would be the behaviour I want; certainly far from
    "nonsense".
    "Inner" doesn't; but the compiler does.
     
    Matthias Buelow, Nov 26, 2007
    #8
  9. 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
     
    Tadeusz B. Kopec, Nov 26, 2007
    #9
  10. 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".
    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.
    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
     
    Victor Bazarov, Nov 26, 2007
    #10
  11. 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.
    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...
    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 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.
     
    Matthias Buelow, Nov 26, 2007
    #11
  12. Matthias Buelow

    James Kanze Guest

    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.
     
    James Kanze, Nov 27, 2007
    #12
  13. Matthias Buelow

    Barry Guest

    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
    {
    };
    };
     
    Barry, Nov 27, 2007
    #13
    1. Advertisements

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 (here). After that, you can post your question and our members will help you out.