std :: set插入与初始化列表

我有这个简单的代码:

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


这里的经验法则是initializer_list<X>重载比其他重载更优先

首先,从[over.ics.list]

如果参数类型是std::initializer_list<X>和初始化列表的所有元素可以隐式转换到X ,隐式转换序列是必要的列表的元素转换为最坏的转换X ,或者如果初始化列表中没有元素,标识转换。 即使在调用初始化程序列表构造函数的上下文中,此转换也可以是用户定义的转换。

而且,从[over.ics.rank]:

列表初始化序列L1是比列表初始化序列L2更好的转换序列
- 对于某些X ,L1转换为std::initializer_list<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>的重载。 12都不能被隐式转换为D1 ,所以过载是不可行的。 现在考虑D1&&过载。 由于我们可以用这个初始化列表构造一个D1 ,这就是所选择的超载,并且我们最终得到一个单元: D1{1, 2}

但是,在这种情况下:

s2.insert({1, 2});

由于D2构造函数中的默认参数, 12都可以隐式转换为D2 。 所以initializer_list<D2>超载是可行的。 D2&&超载也是可行的,但initializer_list转换序列是一个更好的转换序列,所以它是首选。 这给了我们两个元素, D2{1}D2{2}


这是标准行为。 这是因为你正在使用一个initialization listinsert你的set 。 对你的默认参数做的是为每个int创建两个对象(使用第二个参数的缺省值)。

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

上一篇: std::set insert with initialiser lists

下一篇: Remove ScrollBars from QWebEngineView or QWebEnginePage