2014-06-19 12 views
14

Informazioni di base: questo è stato rilevato su Visual Studio 2008 e confermato nuovamente su Visual Studio 2013. G ++ ha urlato al codice, mentre Visual ha accettato la violazione dell'ereditarietà privata in modo silenzioso.Perché auto_ptr sembra violare l'ereditarietà privata su Visual C++?

Così, su Visual C++, abbiamo il seguente codice:

class Base {}; 
class Derived : Base {};  // inherits privately. Adding explicitly the 
           // keyword private changes nothing 

int main() 
{ 
    std::auto_ptr<Base>(new Derived) ; // compiles, which is NOT EXPECTED 
    std::auto_ptr<Base> p(new Derived) ; // Does not compile, which is expected 
} 

Perché sarebbe la prima (temporanea) auto_ptr compilare? Sono andato all'interno di esso in debug, ha fatto esattamente quello che doveva fare con un'eredità pubblica (chiama il costruttore giusto, ecc.)

Chiedendosi se il problema fosse forse con l'implementazione auto_ptr (non si sa mai ...), ho ridotto la questione su questo codice standalone: ​​

class Base {}; 
class Derived : Base {}; 

template <typename T> 
class Ptr 
{ 
    T * m_p; 

    public : 
     Ptr(T * p_p) 
     : m_p(p_p) 
     { 
     } 
} ; 

int main() 
{ 
    Ptr<Base>(new Derived) ; // compiles, which is NOT EXPECTED 
    Ptr<Base> p(new Derived) ; // Does not compile, which is expected 
} 

Anche in questo caso, mi aspettavo il codice per non compilare, in quanto derivata eredita privatamente dalla base.

Ma quando creiamo un temporaneo, funziona.

E non possiamo biasimarlo su std :: auto_ptr.

C'è qualcosa nello standard (o 98 o 11 o 14) che ho perso, o si tratta di un bug?

+0

La mia ipotesi è che 'Ptr (nuova derivata);' non fa come sembra. Sembra che crei un tipo temporaneo di 'Ptr ' costruito con un 'nuovo derivato', ma in realtà dichiara una funzione o alcune assurdità. – nwp

+0

@nwp: questo sarebbe facile da verificare, basta stampare qualcosa dal costruttore e dal distruttore di 'Derived' per verificare se è stato eseguito un codice reale. –

+0

Questo errore è simile al compilatore, ma con 'static_cast': https://connect.microsoft.com/VisualStudio/feedback/details/540343/visual-c-compiler-downcasting-for-private-inheritance-with-static -cast-succeedes – Csq

risposta

3

La conversione Derived* -to- Base*, anche se l'ereditarietà è privata, è consentita in stile C e cast funzionali. E no, in questo caso non significa reinterpret_cast.

Questo non è consentito dallo standard, ma è molto sembra quasi come se fosse consentito, quindi è un bug sottile.

5.2.3 tipo di conversione esplicita (notazione funzionale) [expr.type.conv]

1 [...] Se la lista espressione è una singola espressione, l'espressione tipo di conversione è equivalente (in definizione, e se definito nel significato) per l'espressione cast corrispondente (5.4). [...]

5,4 tipo di conversione esplicita (notazione cast) [expr.cast]

4 Le conversioni eseguite da

  • un const_cast (5.2.11),
  • un static_cast (5.2.9),
  • un static_cast seguito da un const_cast,
  • un reinterpret_cast (5.2.10), o
  • un reinterpret_cast seguito da un const_cast,

possono essere eseguite utilizzando la notazione cast di conversione esplicita.Le stesse restrizioni semantiche e comportamenti applicano, con l'eccezione che in esecuzione di un static_cast nelle seguenti situazioni la conversione è valida anche se la classe base è inaccessibile:

  • un puntatore a un oggetto di tipo classe derivata o un lvalue o rvalue del tipo di classe derivata possono essere esplicitamente convertiti in un puntatore o un riferimento a un tipo di classe base non ambigua, rispettivamente;
  • [...]

Nella situazione che hai, il compilatore interpreta come un static_cast da Derived* a auto_ptr<Base>, e in quel static_cast, un puntatore ad un oggetto di tipo classe derivata viene convertito in un puntatore di un tipo di classe base non ambigua. Quindi sembra che lo standard lo consenta.

Tuttavia, la conversione da Derived* a Base* è implicito, accade solo per essere eseguita come parte di una conversione esplicita diversa. Quindi alla fine, no, lo standard in realtà non lo consente.

Si consiglia di segnalare questo come un bug. Dal commento di Csq, apprendiamo che esiste uno, in cui un esplicito static_cast consente anche questa conversione, ma non è esattamente la stessa cosa. In tal caso, la conversione da Derived* a Base* è esplicita, ma è implicita in questo caso e Visual C++ di solito rifiuta tale conversione implicita.

Si noti che in calchi funzionali che utilizzano molteplici espressioni, questa cattiva interpretazione non è possibile: il compilatore rifiuta correttamente i seguenti:

class Base { }; 
class Derived : Base { }; 

template <typename T> 
class Ptr { 
public: 
    Ptr(T *a, T *b) { } 
}; 

int main() { 
    Ptr<Base>(new Derived, new Derived); 
    // error C2243: 'type cast' : conversion from 'Derived *' to 'Base *' exists, but is inaccessible 
} 
Problemi correlati