2015-09-15 18 views
7

ho questo semplice codice:std :: set inserto con le liste initialiser

struct Base 
{ 
    Base(int xx, int yy) : x(xx), y(yy){} 
    bool operator<(const Base& b) const {return (x < b.x) || (x==b.x && y < b.y);} 

    int x; 
    int y; 
}; 

struct D1 : Base 
{ 
    D1(int x, int y) : Base(x, y){} 
}; 

struct D2 : Base 
{ 
    D2(int x = 0, int y = 0) : Base(x, y){} 
}; 

void test() 
{ 
    std::set<D1> s1; 
    std::set<D2> s2; 

    s1.insert({1, 2}); 
    s2.insert({1, 2}); 

    std::cout<<"s1 size:"<<s1.size()<<std::endl<<"Content:"<<std::endl; 
    for(auto& v : s1) 
    { 
     std::cout<<v.x<<" "<<v.y<<std::endl; 
    } 

    std::cout<<std::endl<<"s2 size:"<<s2.size()<<std::endl<<"Content:"<<std::endl; 
    for(auto& v : s2) 
    { 
     std::cout<<v.x<<" "<<v.y<<std::endl; 
    } 
} 

Con l'uscita:

s1 size:1 
Content: 
1 2 

s2 size:2 
Content: 
1 0 
2 0 

Perché è il comportamento diverso quando l'inserimento di oggetti con argomenti di default? È un bug o è il comportamento previsto?

PS: Potete vedere il codice in azione qui: https://ideone.com/UPArOi

risposta

3

La regola generale qui è che gli overload initializer_list<X> sono fortemente preferito per altri sovraccarichi.

primo luogo, dal [over.ics.list]

se il tipo di parametro è std::initializer_list<X> e tutti gli elementi della lista di inizializzazione può essere convertito in modo implicito X, la sequenza conversione implicita è la conversione peggiore necessario convertire un elemento della lista su X o se l'elenco di inizializzazione non ha elementi, la conversione dell'identità. Questa conversione può essere una conversione definita dall'utente anche nel contesto di una chiamata a un costruttore di inizializzatore-elenco.

E, dal [over.ics.rank]:

sequenza List-inizializzazione L1 è una migliore sequenza conversione di sequenza di inizializzazione lista-L2 se
- L1 converte std::initializer_list<X> per qualche X e L2 non lo fa [...]

Abbiamo due overload rilevanti di std::set::insert:

std::pair<iterator,bool> insert(value_type&& value); 
void insert(std::initializer_list<value_type> ilist); 

Per la prima chiamata:

s1.insert({1, 2}); 

consideri il sovraccarico con il parametro di tipo std::initializer_list<D1>. Né 12 possono essere convertiti implicitamente in D1, in modo che il sovraccarico non sia praticabile. Considerare ora il sovraccarico D1&&. Dato che siamo in grado di costruire un D1 con quell'elenco di inizializzatore, questo è il sovraccarico che è selezionato, e finiamo con un singolo elemento: D1{1, 2}.

Tuttavia, in questo caso:

s2.insert({1, 2}); 

Sia 1 e 2può essere implicitamente convertito in D2, grazie al argomento di default nel costruttore D2 s'.Quindi il sovraccarico dello initializer_list<D2> è praticabile. Anche il sovraccarico di D2&& è praticabile, ma la sequenza di conversione di initializer_list è una sequenza di conversione migliore, quindi è preferibile. Questo ci dà due elementi, D2{1} e D2{2}.

1

Questo è il comportamento standard. È perché stai usando uno initialization list a insert nel tuo set. Quello che fa per il tuo argomento predefinito è creare due oggetti con ogni int (usando il valore predefinito per il secondo argomento).