2015-09-15 7 views
7

ich diesen einfachen Code haben:std :: set Einsatz mit initialiser Listen

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

Mit der Ausgabe:

s1 size:1 
Content: 
1 2 

s2 size:2 
Content: 
1 0 
2 0 

Warum ist das Verhalten anders, wenn Objekte mit Standardargumente einfügen? Ist das ein Fehler oder ist es das beabsichtigte Verhalten?

PS: Sie können den Code in Aktion sehen hier: https://ideone.com/UPArOi

Antwort

3

Die Faustregel hier ist, dass die initializer_list<X> Überladungen stark bevorzugt zu anderen Überlasten sind.

Zuerst von [over.ics.list]

, wenn der Parameter Typ std::initializer_list<X> und alle Elemente der Initialisiererliste ist, kann implizit X, die implizite Konvertierung Sequenz umgewandelt werden ist die schlechteste Umwandlung notwendig, um ein Element der Liste in X zu konvertieren, oder wenn die Initialisierungsliste keine Elemente enthält, die Identitätskonvertierung. Diese Konvertierung kann eine benutzerdefinierte Konvertierung auch im Kontext eines Aufrufs eines Initialisierungslisten-Konstruktors sein.

Und von [over.ics.rank]:

List-Initialisierungssequenz L1 L2 eine bessere Conversion-Sequenz als list-Initialisierungssequenz ist, wenn
- L1 für einige std::initializer_list<X> wandelt X und L2 nicht [...]

Wir haben zwei relevante Überlastungen von std::set::insert:

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

Zum ersten Aufruf:

s1.insert({1, 2}); 

Betrachten Sie die Überlastung mit Parametertyp std::initializer_list<D1>. Weder 1 noch 2 können implizit in D1 konvertiert werden, so dass Überladung nicht praktikabel ist. Betrachten Sie nun die D1&& Überlastung. Da wir mit dieser Initialisierungsliste eine D1 konstruieren können, ist das die Überladung, die ausgewählt wird, und wir enden mit einem einzelnen Element: D1{1, 2}.

jedoch in diesem Fall:

s2.insert({1, 2}); 

Sowohl 1 und 2kann implizit in D2 ‚s Konstruktor D2 dank das Standardargument umgewandelt werden.So ist die Überlastung initializer_list<D2> lebensfähig. Die D2&& Überladung ist ebenfalls praktikabel, aber die initializer_list Konvertierungssequenz ist eine bessere Konvertierungssequenz, daher ist sie bevorzugt. Dies gibt uns zwei Elemente, D2{1} und D2{2}.

1

Dies ist das Standardverhalten. Es ist, weil Sie eine initialization list zu insert in Ihre set verwenden. Was das für Ihre Standardargumente tut, ist das Erstellen von zwei Objekten mit jedem int (mit dem Standardwert für das zweite Argument).