Из-за макета сторонней библиотеки у меня есть что-то вроде следующего кода:

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;
}

Я получаю ошибку компилятора C2247 с MSVC:

Base :: SomeStaticMethod недоступен, поскольку Derived1 использует private для наследования от Base.

Я знаю, что не могу получить доступ к членам Base из Derived2 через наследование из-за частного спецификатора, но я все равно могу вызывать статический метод Base - независимо от каких-либо отношений наследования между Base и Derived2.
Как разрешить двусмысленность и сообщить компилятору, что я просто вызываю статический метод?

25
Carlton 6 Сен 2016 в 16:16

5 ответов

Лучший ответ

Сделай это:

struct Derived2: public Derived1 {
    void SomeInstanceMethod(){
        ::Base::SomeStaticMethod();
//      ^^
//      Notice leading :: for accessing root namespace.
    }
};
22
michalsrb 6 Сен 2016 в 13:26

Пара возможностей:

  1. Не используйте структуру наследования для вызова метода. Используйте ::Base::SomeStaticMethod(), чтобы вызвать его. Base доступен в глобальном пространстве имен.

  2. Переместите функцию private в пространство имен Derived1, написав using Base::SomeStaticMethod;

4
Bathsheba 6 Сен 2016 в 13:22

Я думаю, что ответ michalsrb лучше, но для полноты:

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

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

Тоже будет работать.

8
Martin Bonner supports Monica 6 Сен 2016 в 13:22

Вы можете сделать это, если хотите вызвать его через иерархию:

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

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

В противном случае сделайте, как указано в @michalsrb, если вы хотите вызвать его непосредственно на Base.

5
skypjack 6 Сен 2016 в 13:20

Другие ответы предоставляют способ решения проблемы, я постараюсь объяснить, что происходит. Это из-за injected-class-name .

9.2 (N4594)

[...] Имя класса также вставляется в область видимости самого класса; это известно как внедренное имя-класса. В целях проверки доступа введенное имя-класса обрабатывается так, как если бы оно было публичным именем члена. [...]

Обратите внимание, что даже если вы наберете Base::SomeStaticMethod(), очевидно, что SomeStaticMethod ищется в области Base (это полное имя), но само имя Base также должно быть найдено как-то вверх , (в этом примере как неквалифицированное имя (потому что оно не появляется после оператора разрешения области видимости))

Что происходит, так это то, что когда вы ищете (неквалифицированное) имя Base в Derived2, сначала ищется область Derived2, затем ищется область Derived1, а затем Base выполняется поиск в области видимости и, наконец, обнаруживается injected-class-name . Затем выполняется контроль доступа (поскольку контроль доступа осуществляется после поиска имени), и он обнаруживает, что имя, которое вы искали, является членом Base, который недоступен из {{X6 }}.

7
PcAF 6 Сен 2016 в 18:13