0

A nested class Foo::Utility has access to another nested class Foo::Container even if the later is private. I am trying to extend this access to a polymorphic version UtilityPrint of Foo::Utility without success:

class Foo {
private:
  class Container {};

public:
  class Utility {
  public:
    virtual void action(Container &) = 0;
    // works even if Container is private
  };

  Container container;
  Utility * utility;

  Foo(Utility * utility): container(), utility(utility) {};

  void performAction() {
    utility -> action(container);
  }
};

// polymorphic nested class
// failed attempt
class UtilityPrint : Foo::Utility {
public:
  virtual void action(Foo::Container &) {
    /* Implementation */

    // this does not work, because Foo::Container is private
  }
};

Is there a correct way to achieve this, or is this a bad idea to begin with?

The error message I get is this:

error: ‘class Foo::Container’ is private
   class Container {};
         ^
error: within this context
   virtual void action(Foo::Container &) {

Also, Here is my reason for using this somewhat weird design: I wanted a container and a polymorphic utility that does things to both the container and Foo. Since both container and utility would only be used within the context of Foo, I put the two classes into Foo.


EDIT: I can wrap the derived Utility in a derived Foo, and the code compiles:

class Foo {
protected:
  class Container {};

public:
  class Utility {
  public:
    virtual void action(Container &) = 0;
  };

  Container container;
  Utility * utility;

  Foo(Utility * utility): container(), utility(utility) {};

  void performAction() {
    utility -> action(container);
  }
};

class FooPrint : public Foo {

public:
  class Utility : Foo::Utility {
  public:
    virtual void action(Foo::Container &) {
      /* Implementation */
    }
  };

};

This however introduces a wrapper class FooPrint which exists only for syntactic reasons and is (being a derived class!) never meant to be instantiated. I don't like this approach for this reason, but I can be very wrong on this regard.

Yifan Lai
  • 141
  • 4
  • I would rather put them as standalone classes in the same namespace (especially if they are used as base classes), but that is just my opinion. If you want to keep your design, your only choice is to make `Foo::Container` a public type aswell. – Timo Sep 27 '19 at 20:25

2 Answers2

1

Access is not inherited. This is more often brought up when discussing friends, but it applies here as well.

First, let's look at why Foo::Utility::action can access the private class Foo::Container. Actually, it's right there in the names. The Foo::Container can only be accessed by members of Foo, and members of Foo can be recognized when you write out their qualified names and that name starts with "Foo::".

In contrast, UtilityPrint is not a member of Foo. (In fact, it would be a huge security violation if someone could simply say "oh, yeah, I'm a member too!" and get access to your private information.) So while UtilityPrint has (protected) access to Foo::Utility, it does not have access to everything to which Foo::Utility has access.

If Foo::Utility desires to extend its access to classes derived from it, it would need to explicitly do so. One way to do this is by creating an alias.

class Utility {
  protected:
    using Container = Foo::Container;  // Derived classes can access this.
  public:
    virtual void action(Container &) = 0;
    virtual ~Utility() {}  // <-- remember to properly support polymorphism 
};

That still leaves open the question of whether or not this is good design. I would consider this a warning flag that something might be off, but not conclusively so. The question does not have enough context for me to make this sort of call. I would just give you the guideline that if you feel like you are circumventing a lot of language features (like private access), then maybe the design needs work.

JaMiT
  • 14,422
  • 4
  • 15
  • 31
  • Here is the context of my design: `Foo::Container` is a data structure that is part of the `Foo` implementation, so I made it a private nested class to encapsulate it. And I need some polymorphic utilities that does things to `Container`, but I don't know how to grant access of `Container` to only `Utility` and its offspring without making it public. – Yifan Lai Sep 27 '19 at 21:16
  • I have more than one `utility`s, so making `Foo` polymorphic instead would lead to a lot of diamonds. For the same reason I cant make `Container` nested in `Utility`. – Yifan Lai Sep 27 '19 at 21:19
  • @YifanLai That is information about your current design, not the context of the design. The context I referred to would focus more on what you are trying to accomplish than on how. A comment is probably not long enough to give the necessary context. – JaMiT Sep 27 '19 at 22:37
0

I adopted this solution:

class Foo {
protected:
  class Container {};

public:
  class Utility {
  protected:
    typedef Container FooContainer;

  public:
    virtual void action(Container &) = 0;
  };

  Container container;
  Utility * utility;

  Foo(Utility * utility): container(), utility(utility) {};

  void performAction() {
    utility -> action(container);
  }
};

class UtilityPrint : Foo::Utility {
public:
  virtual void action(FooContainer &) {
    /* implementation */
  }
};

When the name FooContainer is introduced using typedef, accessibility is applied to that name only, with no regard of it actually referring to Foo::Container. This allows all derived Utility to reference Foo::Container by naming FooContainer.

I believe this also applies to using

Yifan Lai
  • 141
  • 4