std :: set insert with initialiser lists

Mir*_*pas 8 c++ overload-resolution c++11

我有这个简单的代码:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

随着输出:

s1 size:1
Content: 
1 2

s2 size:2
Content:
1 0
2 0
Run Code Online (Sandbox Code Playgroud)

插入具有默认参数的对象时,为什么行为会有所不同?这是一个错误还是预期的行为?

PS:你可以在这里看到代码:https://ideone.com/UPArOi

Bar*_*rry 5

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

\n\n

首先,来自[over.ics.list]

\n\n
\n

如果参数类型为std::initializer_list<X>且初始化器列表的所有元素都可以隐式转换为X,则隐式转换序列是将列表的元素转换为\n 所需的最差转换X,或者如果初始化器列表没有元素,身份转换。即使在调用初始值设定项列表构造函数的上下文中,此转换也可以是用户定义的转换。

\n
\n\n

并且,来自 [over.ics.rank]:

\n\n
\n


如果\n \xe2\x80\x94 L1 转换为std::initializer_list<X>某些情况,列表初始化序列 L1 是比列表初始化序列 L2 更好的转换序列X且 L2 不 [...]

\n
\n\n

我们有两个相关的重载std::set::insert

\n\n
std::pair<iterator,bool> insert( value_type&& value );\nvoid insert( std::initializer_list<value_type> ilist );\n
Run Code Online (Sandbox Code Playgroud)\n\n

对于第一次通话:

\n\n
s1.insert({1, 2});\n
Run Code Online (Sandbox Code Playgroud)\n\n

考虑参数 type 的重载std::initializer_list<D1>1也不能隐2式转换为D1,因此重载不可行。现在考虑过D1&&载。由于我们可以使用该初始值设定项列表构造 a D1,因此这就是所选的重载,并且我们最终得到一个元素:D1{1, 2}

\n\n

然而,在这种情况下:

\n\n
s2.insert({1, 2});\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n