3

Say I have an incredibly contrived class defined as follows:

class Outer {
public:
    struct Inner {
        Inner(int innerValue) : foo(innerValue) {};
        int foo;
    };

    Outer(int innerValue) : _inner(innerValue) {};

    const Inner& getInner() const {
        return _inner;
    };

private:
    Inner _inner;
};

I want Outer to be the only class capable of constructing an instance of Inner, but in such a way that other classes/functions can access _inner.foo when passed a reference to _inner through Outer::getInner(). Is this possible? Is there a better way of achieving equivalent functionality?

2
  • Could you make the constructor private and then make Outer a friend of Inner? I don't have time to test that idea at the moment. Commented Jul 12, 2014 at 18:00
  • @JosephMansfield - turns out we can! See leemes' answer below. They posted a second solution, too, which I think is rather novel. Commented Jul 12, 2014 at 19:44

1 Answer 1

6

I'm aware of two solutions to such a problem which I'd like to demonstrate and compare.

Solution 1 (friend):

Using the friend keyword, you can allow another class to access your private functions. (Use carefully.)

You can make the constructor of Inner private, but make Outer a friend of Inner. Note that not only the constructor but any private method of Inner can be called by Outer.

class Outer {
public:
    class Inner {
        friend class Outer;     // <<---- friend

        // private to Inner but also callable by Outer (because it's a friend):
        Inner(int innerValue) : foo(innerValue) {};

    public:
        // (add public methods here)
        int foo;
    };

    Outer(int innerValue) : _inner(innerValue) {};

    const Inner& getInner() const {
        return _inner;
    };

private:
    Inner _inner;
};

Solution 2 (access keys):

A different approach without friend but slightly more complex is to give the constructor a parameter which only Outer is able to construct (like a "key"). The constructor is then public, but other classes will not be able to call it since they can't provide the arguments required to do so:

class Outer {
    // Our dummy struct which is only constructible by Outer:
    struct ConstructInnerKey {
        // Empty
    };

public:
    struct Inner {
        Inner(int innerValue, Outer::ConstructInnerKey) : foo(innerValue) {};
        //                    ^^^^^^^^^^^^^^^^^^^^^^^^
        //                a key is required (but not used)
        //                to construct an instance of Inner

        // (add public methods here)
        int foo;
    };

    Outer(int innerValue) : _inner(innerValue, Outer::ConstructInnerKey()) {};
    //                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^
    //                                      make a key and pass it to Inner

    const Inner& getInner() const {
        return _inner;
    };

private:
    Inner _inner;
};

Note that while this looks like it adds a performance overhead, this is not the case. Everything is optimized away and only serves for catching access violations during compilation as you'd like to.

Comparison:

Solution 2 has the advantage that Inner only grants Outer to access the constructor. So as long as you want to have "truly" private functions in Inner (not even accessible by Outer), this is a good solution. But solution 1 is a lot easier to write and understand (quick and dirty, so to speak).

Sign up to request clarification or add additional context in comments.

3 Comments

Ah, duh! That will probably work for me - giving this a less contrived attempt right now...
@Nate I added an alternative approach :)
Sneaky. In my use-case the friend version will work fine, as no other private functions exist in Inner - I'll definitely keep the access key solution in mind for future problems, though. Thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.