我有这个简单的代码:
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
答案 0 :(得分:4)
这里的经验法则是initializer_list<X>
重载强烈偏好到其他重载。
首先,来自[over.ics.list]
如果参数类型为
std::initializer_list<X>
且初始化列表的所有元素都可以 隐式转换为X
,隐式转换序列是转换转换所需的最差转换 列表的元素为X
,或者如果初始化列表没有元素,则为标识转换。这种转换可以 即使在调用初始化列表构造函数的上下文中,也是用户定义的转换。
而且,来自[over.ics.rank]:
如果是,则列表初始化序列L1是比列表初始化序列L2更好的转换序列 - 对于某些
std::initializer_list<X>
,L1转换为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
。所以initializer_list<D2>
重载是可行的。 D2&&
重载也是可行的,但initializer_list
转换序列是更好的转换序列,因此它是首选。这为我们提供了两个元素,D2{1}
和D2{2}
。
答案 1 :(得分:1)
这是标准行为。这是因为您在initialization list
中使用了insert
到set
。对默认args的作用是为每个int
创建两个对象(使用第二个arg的默认值)。