2015-09-15 2 views
7

у меня есть этот простой код:станд :: установлен вкладыш со списками Инициализатора

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

С выходом:

s1 size:1 
Content: 
1 2 

s2 size:2 
Content: 
1 0 
2 0 

Почему поведение отличается при вставке объектов с аргументами по умолчанию? Это ошибка или это предполагаемое поведение?

PS: Вы можете увидеть код в действии здесь: https://ideone.com/UPArOi

ответ

3

Правило большого пальца в том, что перегрузки initializer_list<X>: настоятельно рекомендуется другим перегрузкам.

Во-первых, из [over.ics.list]

если тип параметра std::initializer_list<X> и все элементы списка инициализатора могут быть неявно преобразуется в X, неявный последовательность преобразования является худшим преобразования необходимо преобразовать элемент из списка в X, или если в списке инициализаторов нет элементов, преобразование идентичности. Это преобразование может быть быть определяемым пользователем преобразованием даже в контексте вызова конструктора списка инициализаторов.

И, из [over.ics.rank]:

последовательности List-инициализация L1 является лучшей последовательностью преобразования, чем последовательность списка инициализации L2, если
- L1 превращается в std::initializer_list<X> для некоторых X и L2 не [...]

у нас есть две соответствующие перегруженные std::set::insert:

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

Для первого вызова:

s1.insert({1, 2}); 

Рассмотрим перегрузку с типом параметра std::initializer_list<D1>. Ни 1, ни 2 могут быть неявно преобразованы в D1, так что перегрузка не является жизнеспособной. Теперь рассмотрим перегрузку D1&&. Поскольку мы можем построить D1 с этим списком инициализаторов, это выбранная перегрузка, и мы получим один элемент: D1{1, 2}.

Однако, в данном случае:

s2.insert({1, 2}); 

Оба 1 и 2могут быть неявно преобразован в D2, благодаря аргументу по умолчанию в конструкторе D2 «s.Таким образом, перегрузка initializer_list<D2> жизнеспособна. Перегрузка D2&& также жизнеспособна, но последовательность преобразования initializer_list является лучшей последовательностью преобразования, поэтому она является предпочтительной. Это дает нам два элементов, D2{1} и D2{2}.

1

Это стандартное поведение. Это потому, что вы используете initialization list до insert в свой set. Что для ваших аргументов по умолчанию является создание двух объектов с каждым int (используя значение по умолчанию для второго arg).

Смежные вопросы