7

I'm wondering why a call to a static function is ambiguous, even when one of the two is obviously impossible to call as it is private. I was hoping I could use private / protected inheritance to help the compiler solve the ambiguity.

Is it specific to MSVC or is it somehow specified in the standard ?

struct A
{
    static int num() { return 0; }
};

struct B
{
    static int num() { return 1; }
};

struct C : public A, private B
{};

int main()
{
     C::num(); // Ambiguous access of num
}

The background is that I was trying a way of reusing an overloading behavior (the one in A) in many derived classes (C,D,E,F,G) by inheriting it, to adhere somehow to a rule of Don't Repeat Yourself.

N0vember
  • 995
  • 1
  • 9
  • 12

3 Answers3

8

Yes it is specified in the C++ Standard, section §3.4 [basic.lookup]

The access rules (Clause 11) are considered only once name lookup and function overload resolution (if applicable) have succeeded

Name lookup doesn't care about accessibility : it finds both A::num and B::num, so there is an ambiguity for the compiler.

You can explicitly call A::num with :

C::A::num();

If you explicitly try to call B::num, then your compiler will indeed emit an access error :

C::B::num(); // Error

You can also explicitly bring the base name into scope within the derived class, which will fix the ambiguity :

struct C : public A, private B
{
    using A::num;    
};
quantdev
  • 23,517
  • 5
  • 55
  • 88
  • I understand. So I can explicitly solve ambiguity in the derived, on a per-function basis, but there is no way to specify all members of one base class would take precedence over the other, which is what I was naively trying to achieve. The only way is by overloading 'vertically' if that makes any sense ? – N0vember Dec 28 '14 at 19:15
  • Yes, that's correct. But generally speaking, you should first try to avoid name clashing (talking about non virtual members, of course), so the problem doesn't occur in the first place. – quantdev Dec 28 '14 at 19:16
  • The real life example is just slightly more complex : many derived have to overload `A::num`, but for the sake of verbosity and `DRY` they inherit the overload from another base class. I'll go with the `using` solution as it is the less verbose. – N0vember Dec 28 '14 at 19:27
3

To help compiler you can do this

 struct C : public A, private B
 {
      using A::num;
 };
mip
  • 8,355
  • 6
  • 53
  • 72
2

Private members are intentionally considered for overload resolution.

Suppose you have

class C {
public:
  static void f(int);
  static void g();
private:
  static void f(long);
};

void C::g() {
  f(0L); // okay
}
void g() {
  f(0L); // error, does not silently call f(int)
}

Making only public members available during overload resolution causes highly surprising re-interpretations of code, where code that looks like it should work the exact same way would silently call different overloads.

Making such code an error was considered less troublesome than the alternative.