2013-01-24 17 views
10

Desidero eseguire il bind() sulla versione della funzione della classe base della classe derivata. La funzione è contrassegnata protetta nella base. Quando faccio così, il codice viene compilato felicemente in Clang (Apple LLVM Compiler 4.1) ma dà un errore sia in g ++ 4.7.2 che in Visual Studio 2010. L'errore è simile a: "'Base :: foo': impossibile accesso protetto membro. "std :: bind() - utilizzo di una funzione membro protetto di base dalla funzione membro di una classe derivata

L'implicazione è che il contesto per il riferimento è effettivamente all'interno di bind(), dove ovviamente la funzione è vista come protetta. Ma non dovrebbe bind() ereditare il contesto della funzione di chiamata - in questo caso, Derived::foo() - e quindi vedere il metodo di base come accessibile?

Il seguente programma illustra il problema.

struct Base 
{ 
protected: virtual void foo() {} 
}; 

struct Derived : public Base 
{ 
protected: 
    virtual void foo() override 
    { 
     Base::foo();      // Legal 

     auto fn = std::bind(&Derived::foo, 
      std::placeholders::_1);  // Legal but unwanted. 
     fn(this); 

     auto fn2 = std::bind(&Base::foo, 
      std::placeholders::_1);  // ILLEGAL in G++ 4.7.2 and VS2010. 
     fn2(this); 
    } 
}; 

Perché la discrepanza nel comportamento? Che è corretto? Quale soluzione alternativa è disponibile per i compilatori che generano errori?

+0

È intenzionale che "Derived :: foo" si chiami o sia solo un risultato della semplificazione per un esempio? – aschepler

+0

@aschepler Questa è la parte "indesiderata" di "legale ma indesiderata". – OldPeculier

risposta

9

Risposta: vedi boost::bind with protected members & context che cita questa parte del standard

viene applicato un controllo di accesso ulteriore al di là di quelli descritti in precedenza nella clausola 11 quando un membro di dati non statico o funzione membro non static è un protetti membro della sua classe di denominazione (11.2) 105) Come descritto in precedenza , l'accesso ad un membro protetto è concesso perché il riferimento si verifica in un amico o membro di qualche classe C. Se l'accesso è di formare un puntatore al membro (5.3 .1), l'identificatore del nome nidificato deve nominare C o ac lass derivato da C. Tutti gli altri accessi implicano un'espressione (forse implicita) (5.2.5). In questo caso, la classe dell'espressione oggetto è C o una classe derivata da C.

Soluzione: fare foo una funzione public membro

#include <functional> 

struct Base 
{ 
public: virtual void foo() {} 
}; 
+3

Se non è possibile modificare 'Base', adattarlo con un metodo derivato (ad esempio' void basefoo() {Base :: foo();} '). Sorprendentemente e confusamente in Visual C++ (Express 2010), puoi effettivamente usare '& Derived :: Base :: foo' per aggirare questo errore, ma in pratica ignora il tuo secondo argomento (cioè, usa sempre' this'). –

9

Questo non ha nulla a che fare con bind. A causa del pezzo dello standard @rhalbersma già citato, l'espressione &Base::foo è illegale in un membro non amico di Derived, in ogni contesto.

Ma se il tuo intento era quello di fare qualcosa di equivalente a chiamare Base::foo();, avete un problema più grande: i puntatori alle funzioni membro sempre invocano una sostituzione virtuale.

#include <iostream> 

class B { 
public: 
    virtual void f() { std::cout << "B::f" << std::endl; } 
}; 

class D : public B { 
public: 
    virtual void f() { std::cout << "D::f" << std::endl; } 
}; 

int main() { 
    D d; 
    d.B::f(); // Prints B::f 

    void (B::*ptr)() = &B::f; 
    (d.*ptr)(); // Prints D::f! 
} 
Problemi correlati