2016-03-28 17 views
6

Desidero eseguire il std::bind per una funzione membro da una classe di base privata, resa "pubblica" con una dichiarazione using nella classe derivata. Chiamando la funzione lavora direttamente, ma sembra vincolante o usando puntatori a funzione membro non può essere compilato:Associazione alla funzione membro ereditato privatamente

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

Guardando i messaggi di errore di cui sopra, il problema sembra essere che Derived::foo è ancora solo Base::foo, e posso' t accedere a Base tramite Derived all'esterno dello Derived stesso.

Questo sembra incoerente - non dovrei essere in grado di utilizzare le chiamate dirette, le funzioni associate e i puntatori di funzione in modo intercambiabile?

C'è una soluzione che mi permetteva di legarsi a foo su un oggetto Derived, preferibilmente senza cambiare Base o Derived (che sono in una biblioteca che non possiedo)?

+0

Come soluzione (un po 'brutta) è possibile utilizzare un lambda '[& d]() {d.foo(); } '. – Holt

risposta

4

Il problema qui è ciò che il dichiarazione using realmente fa:

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

che porta Base::foo in ambito pubblico in Derived, ma non crea una funzione completamente nuova. E 'Non equivalente ad aver scritto:

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

C'è ancora solo Base::foo(). La dichiarazione d'uso riguarda semplicemente le regole di accesso e le regole di risoluzione di sovraccarico. Come tale &Derived::foo ha davvero tipo void (Base::*)() (e non void (Derived::*)()!), Poiché questo è l'unico foo esistente. Poiché Base è private, l'accesso dei membri tramite un puntatore a Base non è corretto. Sono d'accordo che questo è abbastanza sfortunato ("incoerente" è una buona parola).

È ancora possibile creare un oggetto funzione che chiama foo. Non puoi semplicemente usare il puntatore al membro. Con C++ 14, questo diventa semplice se verbose (sto assumendo argomenti arbitrari qui e che void foo() è solo una semplificazione del problema):

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

con C++ 11, che avrebbe dovuto scrivere un tipo con un modello variadiale operator().

+0

Bello, usare lambda è un modo interessante per evitare il puntatore! C'è qualche soluzione che non richiede C++ 11? –

+0

@AndersJohansson Stai già usando 'std :: bind'? Ad ogni modo, qualsiasi lambda può sempre essere scritta come una struct 'struct equivalente D_Foo {D d; void operator()() {d.foo(); }}; ' – Barry

+0

Il mio test case è C++ 11 con' std :: bind', ma in produzione sono limitato a C++ 03 con 'boost :: bind' - Ho mantenuto la domanda semplice perché gli errori sono gli stessi . Suona come le strutture come sopra dovrebbe fare il trucco. Grazie! –