Using declaration for overloaded inherited function with private accessibility

Viewed 414

I have a class that looks something like this:

class A
{
public:
    void foo(int arg) { foo(arg, false); }
private:
    void foo(int arg, bool flag) {}
};

It is built this way because I want foo's flag argument to only be false when called from outside A. I want to inherit it privately, but allow calling foo:

class B : private A
{
public:
    using A::foo;
};

However, this fails because the using declaraion attempts to bring all the overloads of foo into scope, including the private one, which the the compiler rightly rejects.

This isn't hard to fix, I can either:

  1. Change the accessibility of A::foo(int, bool) to protected or public
  2. Inherit A publicly; only public overloads of foo will be inherited
  3. Change the name of A::foo(int, bool) so that the using declaration does not attempt to bring it into scope

This is a small, private project, and besides, that overload is only called inside A. So fixing the problem is a non-issue here. (I'm just going to rename the private overload.)

But it doesn't feel like it should be necessary. Why does the using declaration attempt to bring the non-accessible methods into scope? Is this particular case just not covered by the standard? Is there a way to fix this besides the methods I listed?

2 Answers

You can also redefine the overload you want and have it forward its argument to the function in A:

class B : private A
{
public:
    void foo(int arg) { A::foo(arg); }
};

The using declaration is just too blunt a tool in this case. It brings function names into the derived class scope. And when the name refers to something private, it chokes. It can't distinguish overloads. The standard requires the names introduced by a using declaration to be accessible:

[namespace.udecl]/17

In a using-declarator that does not name a constructor, all members of the set of introduced declarations shall be accessible. In a using-declarator that names a constructor, no access check is performed. In particular, if a derived class uses a using-declarator to access a member of a base class, the member name shall be accessible. If the name is that of an overloaded member function, then all functions named shall be accessible. The base class members mentioned by a using-declarator shall be visible in the scope of at least one of the direct base classes of the class where the using-declarator is specified.


The forwarding function can also be templated. So one won't need to redefine each function they want to expose individually.

class B : private A
{
public:
    template<typename ...Args>
    void foo(Args ...args) { A::foo(args...); }
};

It's "catch-all" like the using declaration, except the access specifier is checked only on template instantiation, i.e. when the function is called. So the template will be ill-formed based on its scope and whether or not the member in A is accessible there.

I found the following extract from Scott Meyer's Effective C++ which is related to your predicament (with emphasis added):

Item 33: Avoid hiding inherited names.
...
This means that if you inherit from a base class with overloaded functions and you want to redefine or override only some of them, you need to include a using declaration for each name you’d otherwise be hiding. If you don’t, some of the names you’d like to inherit will be hidden.
...

It’s conceivable that you sometimes won’t want to inherit all the functions from your base classes. Under public inheritance, this should never be the case, because, again, it violates public inheritance’s is-a relationship between base and derived classes. (That’s why the using declarations above are in the public part of the derived class: names that are public in a base class should also be public in a publicly derived class.)

Under private inheritance, however, it can make sense. For example, suppose Derived privately inherits from Base, and the only version of the function that Derived wants to inherit is the one taking no parameters. A using declaration won’t do the trick here, because a using declaration makes all inherited functions with a given name visible in the derived class.
No, this is a case for a different technique, namely, a simple forwarding function:

class Base {
public:
   virtual void mf1() = 0;
   virtual void mf1(int);
... // as before
};
class Derived: private Base {
public:
   virtual void mf1() // forwarding function; implicitly
   { 
      Base::mf1(); } // inline 
   }; 
}
Related