2016-03-28 6 views
6

Ich möchte std::bind zu einer Member-Funktion aus einer privaten Basisklasse, die mit einer using-Deklaration in der abgeleiteten Klasse "public" gemacht. Der Aufruf der Funktion direkt funktioniert, aber es scheint Bindung oder Mitglied Funktionszeiger mit nicht kompiliert:Bindung an privat geerbt Mitglied Funktion

#include <functional> 

struct Base { 
    void foo() { } 
}; 

struct Derived : private Base { 
    using Base::foo;    
}; 

int main(int, char **) 
{ 
    Derived d; 

    // call member function directly: 
    // compiles fine 
    d.foo(); 

    // call function object bound to member function: 
    // no matching function for call to object of type '__bind<void (Base::*)(), Derived &>' 
    std::bind(&Derived::foo, d)(); 

    // call via pointer to member function: 
    // cannot cast 'Derived' to its private base class 'Base' 
    (d.*(&Derived::foo))(); 

    return 0; 
} 

oben bei den Fehlermeldungen Blick scheint das Problem zu sein, dass Derived::foo noch Base::foo ist einfach, und ich kann‘ t Zugang Base durch Derived außerhalb Derived selbst.

Dies scheint inkonsistent - sollte ich nicht in der Lage sein, direkte Aufrufe, gebundene Funktionen und Funktionszeiger austauschbar zu verwenden?

Gibt es eine Abhilfe, die mich zu foo auf ein Derived Objekt binden lassen würde, vorzugsweise ohne Base oder Derived Wechsel (die in einer Bibliothek, die ich nicht gehört)?

+0

Als (etwas hässlicher) Workaround könnten Sie ein Lambda '[& d]() {d.foo(); } '. – Holt

Antwort

4

Das Problem hier ist, was die mit Deklaration tatsächlich tut:

struct Derived : private Base { 
    using Base::foo;    
}; 

Das Base::foo in öffentlichen Rahmen in Derived bringt, aber es eine völlig neue Funktion nicht schaffen wird. Es ist nicht entspricht geschrieben zu haben:

struct Derived : private Base { 
    void foo() { Base::foo(); } 
} 

Es gibt immer noch nur Base::foo(). Die using-declaration wirkt sich nur auf die Zugriffsregeln und die Überladungsauflösungsregeln aus. Als solcher hat &Derived::foo wirklich Typ void (Base::*)() (und nicht void (Derived::*)()!), Da das der einzige foo ist, der existiert. Da Baseprivate ist, ist der Mitgliederzugriff über einen Zeiger auf Base fehlerhaft. Ich stimme zu, dass dies ziemlich bedauerlich ist ("inkonsistent" ist ein gutes Wort).

Sie können weiterhin ein Funktionsobjekt erstellen, das foo aufruft. Sie können den Zeiger zum Mitglied nicht verwenden. Mit C++ 14, wird diese einfach, wenn die ausführliche (ich gehe davon hier beliebige Argumente und dass void foo() lediglich eine Vereinfachung des Problems):

auto d_foo = [d](auto&&... args){ return d.foo(std::forward<decltype(args)>(args)...); } 

mit 11 C++, man müßte schreiben ein Typ mit einer variadischen Vorlage operator().

+0

Schön, mit Lambdas ist eine interessante Möglichkeit, den Zeiger zu vermeiden! Gibt es eine Problemumgehung, die C++ 11 nicht erfordert? –

+0

@AndersJohansson Sie verwenden bereits 'std :: bind'? Jedenfalls kann jedes Lambda immer als äquivalente Struktur geschrieben werden. Struct D_Foo {D d; void operator()() {d.foo(); }}; ' – Barry

+0

Mein Testfall ist C++ 11 mit' std :: bind', aber in der Produktion bin ich auf C++ 03 mit 'boost :: bind' beschränkt - ich behalte die Frage einfach, weil die Fehler gleich sind . Klingt wie Strukturen wie die oben genannten sollten den Trick tun. Vielen Dank! –