25

Due to the layout of a third-party library, I have something like the following code:

struct Base
{
    static void SomeStaticMethod(){}
};

struct Derived1: private Base {};

struct Derived2: public Derived1 {
    void SomeInstanceMethod(){
        Base::SomeStaticMethod();
    }
};

int main() {
    Derived2 d2;
    d2.SomeInstanceMethod();

    return 0;
}

I'm getting compiler error C2247 with MSVC:

Base::SomeStaticMethod not accessible because Derived1 uses private to inherit from Base.

I know I can't access Base members from Derived2 via inheritance because of the private specifier, but I should still be able to call a static method of Base - regardless of any inheritance relationship between Base and Derived2.
How do I resolve the ambiguity and tell the compiler I'm just making a call to a static method?

skypjack
  • 49,335
  • 19
  • 95
  • 187
Carlton
  • 4,217
  • 2
  • 24
  • 40

5 Answers5

22

Do this:

struct Derived2: public Derived1 {
    void SomeInstanceMethod(){
        ::Base::SomeStaticMethod();
//      ^^
//      Notice leading :: for accessing root namespace.
    }
};
michalsrb
  • 4,703
  • 1
  • 18
  • 35
  • This doesn't work (same error C2247). I'm using MSVC 2013 if that's relevant. – Carlton Sep 06 '16 at 13:24
  • 1
    Are you sure? It *should* work? Did you write the leading `::`? – Bathsheba Sep 06 '16 at 13:25
  • Positive. I copied/pasted your code and cleaned/rebuilt my project. – Carlton Sep 06 '16 at 13:27
  • It does fix your example when compiled with gcc (4.8 and 6) and with clang(3.7.0). – michalsrb Sep 06 '16 at 13:30
  • Confirmed on my computer - it works with GCC but not MSVC. I'm marking this as the accepted answer anyway. – Carlton Sep 06 '16 at 13:34
  • 19
    Bill Gates created the MSVC bug, Gill Bates found it :-) this made my day – hannibal Sep 06 '16 at 14:24
  • @Carlton Does doing `::Base b; b.SomeStaticMethod()` work on MSVC? – David G Sep 06 '16 at 15:26
  • @0x499602D2 Yes, that does work. It seems that the compiler resolves the class name properly when doing a declaration, but not when calling the static method. On a side note, omitting the leading `::` produces the same C2247 error for your snippet. – Carlton Sep 06 '16 at 15:37
8

I think michalsrb's answer is better, but for completeness:

namespace
{
    void SomeStaticMethodProxy()
    {
        return Base::SomeStaticMethod();
    }
}

struct Derived2: public Derived1 {
    void SomeInstanceMethod(){
        SomeStaticMethodProxy();
    }
};

will also work.

8

Other answers provide way to solve the problem, I'll try to explain what's happening. It's because of injected-class-name.

9.2 (N4594)

[...]The class-name is also inserted into the scope of the class itself; this is known as the injected-class-name. For purposes of access checking, the injected-class-name is treated as if it were a public member name.[...]

Note that even if you type Base::SomeStaticMethod(), obviously SomeStaticMethod is looked up in Base scope (It's qualified name), but name Base itself also has to be looked up somehow, (In this example as an unqualified name (because it does not appear after scope resolution operator))

What happens is that when you search for (unqalified) name Base in Derived2, first Derived2 scope is searched, then Derived1 scope is searched and then Base scope is searched and finally injected-class-name is found. Then access control takes place (because access control takes place after name lookup) and it'll find that name you looked up is Base's member which isn't accessible from Derived2.

PcAF
  • 1,975
  • 12
  • 20
  • Brilliant! I sort of intuited that something like this must be going on, but this makes it very clear *exactly* what is happening (and explains why putting `::` on the front fixes the problem). – Martin Bonner supports Monica Sep 06 '16 at 19:56
6

You can do this if you want to call it through the hierarchy:

struct Derived1: private Base {
protected:
    using Base::SomeStaticMethod;
};

struct Derived2: public Derived1 {
    void SomeInstanceMethod(){
        Derived1::SomeStaticMethod();
    }
};

Otherwise, do as @michalsrb mentioned if you want to call it directly on Base.

skypjack
  • 49,335
  • 19
  • 95
  • 187
4

A couple of possibilities:

  1. Don't use the inheritance structure to call the method. Use ::Base::SomeStaticMethod() to call it. Base is accessible in the global namespace.

  2. Bring the private function into the namespace of Derived1 by writing using Base::SomeStaticMethod;

Bathsheba
  • 231,907
  • 34
  • 361
  • 483