std::set insert with initialiser lists

I have this simple code:

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

With the output:

s1 size:1
Content: 
1 2

s2 size:2
Content:
1 0
2 0

Why is the behaviour different when inserting objects with default arguments? Is this a bug or is it the intended behaviour?

PS: You can see the code in action here: https://ideone.com/UPArOi


The rule of thumb here is that the initializer_list<X> overloads are strongly preferred to other overloads.

First, from [over.ics.list]

if the parameter type is std::initializer_list<X> and all the elements of the initializer list can be implicitly converted to X , the implicit conversion sequence is the worst conversion necessary to convert an element of the list to X , or if the initializer list has no elements, the identity conversion. This conversion can be a user-defined conversion even in the context of a call to an initializer-list constructor.

And, from [over.ics.rank]:

List-initialization sequence L1 is a better conversion sequence than list-initialization sequence L2 if
— L1 converts to std::initializer_list<X> for some X and L2 does not [...]

We have two relevant overloads of std::set::insert :

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

For the first call:

s1.insert({1, 2});

Consider the overload with parameter type std::initializer_list<D1> . Neither 1 nor 2 can be implicitly converted to D1 , so that overload is not viable. Now consider the D1&& overload. Since we can construct a D1 with that initializer list, that's the overload that's selected, and we end up with a single element: D1{1, 2} .

However, in this case:

s2.insert({1, 2});

Both 1 and 2 can be implicitly converted to D2 , thanks to the default argument in D2 's constructor. So the initializer_list<D2> overload is viable. The D2&& overload is viable as well, but the initializer_list conversion sequence is a better conversion sequence, so it's preferred. This gives us two elements, D2{1} and D2{2} .


This is standard behavior. It's because you're using an initialization list to insert into your set . What that does for your default args is to create two objects with each int (using the defaulted value for the second arg).

链接地址: http://www.djcxy.com/p/87856.html

上一篇: 针对最新的iPad Pro模拟器进行优化

下一篇: std :: set插入与初始化列表