15

In questo momento sto imparando le funzionalità di Inheritance in C++ e ho voluto testare il concetto delle classi di base virtuale apprese di recente. Ho provato il seguente codice semplice:Perché l'inizializzazione uniforme in C++ 11 si comporta stranamente con le classi di base virtuali?

#include <iostream> 

using namespace std; 

class A 
{ 
private: 
    int m_value; 
    string m_caller; 
public: 
    A(int p_value, string p_caller) : m_value{p_value}, m_caller{p_caller} 
    { 
     cout<<"Instantiating A via "<<m_caller<<endl; 
    } 
}; 

class B : virtual public A 
{ 
private: 
    int m_value; 
public: 
    B(int p_value1,int p_value2) : A{p_value1,"B"}, m_value{p_value2} 
    { 
     cout<<"Instantiating B."<<endl; 
    } 
}; 

class C : public B 
{ 
public: 
    C(int p_value1,int p_value2) : A{p_value1,"C"}, B(p_value1, p_value2) 
    { 
     cout<<"Instantiating C."<<endl; 
    } 
}; 

int main() 
{ 
    C c1(1,2); 
    return 0; 
} 

Si prega di notare la B(p_value1, p_value2) nel costruttore della classe C. Questo mi ha dato l'output desiderato:

Instantiating A via C 
Instantiating B. 
Instantiating C. 

Ma, il momento in cui ho cambiato in B{p_value1, p_value2}, ho ottenuto il seguente risultato:

Instantiating A via C 
Instantiating A via B 
Instantiating B. 
Instantiating C. 

ho provato a cercare la risposta, ma tutte le risposte mi sono citato alcuni standard C++ . Essendo un principiante in OOP, sto cercando una spiegazione più semplice per questo comportamento. Grazie mille!

P.S. Sto usando C :: B in Windows con il compilatore g ++ 4.8.1.

+3

Fyi, clang ++ 3.7 non mostra il comportamento che stai descrivendo. Entrambe le versioni emettono lo stesso risultato (il primo). – WhozCraig

+0

Si sta chiamando il costruttore di copie nel secondo caso. – lorro

+3

@UpAndAdam La classe più derivata è responsabile per l'inizializzazione delle classi di base virtuali, il codice non verrebbe compilato se l'OP non chiamasse direttamente il costruttore 'A'. E di cosa si tratta * passando in letterali stringa convertiti come se fossero ints *? – Praetorian

risposta

8

Questo è un bug del compilatore in g ++.

In 14 sezione (N4140) [dcl.init.list], la definizione di elenco inizializzazione (modificato per concisione) C++:

List-inizializzazione di un oggetto o di riferimento di tipo T è definita come segue:

  • Se T è un aggregato, inizializzazione aggregato viene eseguita
  • Altrimenti, se l'elenco di inizializzazione non ha elementi e T è un tipo di classe con un costruttore di default , l'oggetto è inizializzato in base al valore.
  • In caso contrario, se T è una specializzazione di std :: initializer_list, [...]
  • In caso contrario, se T è un tipo di classe, i costruttori sono considerati. I costruttori applicabili sono enumerati e il migliore viene scelto tramite la risoluzione di sovraccarico. Se è necessaria una conversione restringente per convertire uno qualsiasi degli argomenti, il programma è mal formato.
  • [...]

I primi 3 punti non sono applicabili: B non è un aggregato (aggregato non può avere classi di base), la lista di inizializzazione ha elementi, B non è una specializzazione di std::initializer_list.

Il quarto punto si applica perché la risoluzione di sovraccarico corrisponde B{p_value1, p_value2} al costruttore B(int, int) secondo [over.match.list] /1.2:

Se non viene trovato nessun costruttore di inizializzazione-list praticabile, viene eseguita la risoluzione di sovraccarico di nuovo, dove le funzioni candidate sono tutti i costruttori della classe T e l'elenco degli argomenti è costituito dagli elementi dell'elenco di inizializzazione.

Dall'ultima citazione risulta che B(whatever) e B{whatever} devono comportarsi in modo identico.

+0

Risposta perfetta! Grazie :) – SimplyOm