如果我尝试为std::set
使用统一的初始化程序,则会得到不同的结果。
示例:
int main()
{
std::array a {1,2,3,4};
std::set<int> s1 {a.begin(), a.end()};
std::set s2 {a.begin(), a.end()};
std::set s3 (a.begin(), a.end());
for(auto& i: s1) { std::cout << i << "\n"; }
std::cout << "####" << std::endl;
for(auto& i: s2) { std::cout << i << "\n"; }
std::cout << "####" << std::endl;
for(auto& i: s3) { std::cout << i << "\n"; }
}
结果:
1
2
3
4
####
0x7ffecf9d12e0
0x7ffecf9d12f0
####
1
2
3
4
这似乎与“推论指南”有关,如果与{}
或()
语法一起使用,则对它们的评估会有所不同。
答案 0 :(得分:7)
对于s2
,使用大括号语法,并且{a.begin(), a.end()}
被认为是initializer_list
中的std::array<int>::iterator
。因此,s2
是一组迭代器。
对于s3
,使用括号语法,并选择迭代器构造函数。 s3
是一组int
,并且从[a.begin(), a.end())
范围初始化。
每[set.overview],我们有两个与之相关的推导指南:
template<class InputIterator, class Compare = less<typename iterator_traits<InputIterator>::value_type>, class Allocator = allocator<typename iterator_traits<InputIterator>::value_type>> set(InputIterator, InputIterator, Compare = Compare(), Allocator = Allocator()) -> set<typename iterator_traits<InputIterator>::value_type, Compare, Allocator>;
和
template<class Key, class Compare = less<Key>, class Allocator = allocator<Key>> set(initializer_list<Key>, Compare = Compare(), Allocator = Allocator()) -> set<Key, Compare, Allocator>;
形成一组功能和功能模板,包括:
[...]
(1.4)对于每个推演指南,具有以下属性的函数或函数模板:
模板参数(如果有)和功能参数是 deduction-guide 的参数。
返回类型是推导指南的 simple-template-id 。
在这种情况下,上述推导指南的综合功能和功能模板分别为
template<class InputIterator,
class Compare = less<typename iterator_traits<InputIterator>::value_type>,
class Allocator = allocator<typename iterator_traits<InputIterator>::value_type>>
auto __func1(InputIterator, InputIterator,
Compare = Compare(), Allocator = Allocator())
-> set<typename iterator_traits<InputIterator>::value_type, Compare, Allocator>;
和
template<class Key, class Compare = less<Key>, class Allocator = allocator<Key>>
auto __func2(initializer_list<Key>, Compare = Compare(), Allocator = Allocator())
-> set<Key, Compare, Allocator>;
(我用双下划线表示这些名称是合成的,否则无法访问。)
初始化和过载解析按照 [dcl.init]和[over.match.ctor],[over.match.copy]或 [over.match.list](适用于初始化类型 对于假设类类型的对象,其中 所选功能和功能模板被认为是 该类类型的构造函数,以形成重载 设置,并且初始化器由哪个类的上下文提供 执行模板参数推导。每个这样的概念 如果该函数或函数认为构造函数是显式的 模板是根据构造函数或 deduction-guide 被声明为
explicit
。所有此类概念构造器均被考虑 成为假设的类类型的公共成员。
假设的类类型如下:
class __hypothetical {
public:
// ...
// #1
template<class InputIterator,
class Compare = less<typename iterator_traits<InputIterator>::value_type>,
class Allocator = allocator<typename iterator_traits<InputIterator>::value_type>>
__hypothetical(InputIterator, InputIterator,
Compare = Compare(), Allocator = Allocator())
-> set<typename iterator_traits<InputIterator>::value_type, Compare, Allocator>;
// #2
template<class Key, class Compare = less<Key>, class Allocator = allocator<Key>>
__hypothetical(initializer_list<Key>, Compare = Compare(), Allocator = Allocator())
-> set<Key, Compare, Allocator>;
// ...
};
对于s2
的声明,
std::set s2 {a.begin(), a.end()};
重载解析的执行就像在
中一样__hypothetical __hyp{a.begin(), a.end()}; // braces
因此,[over.match.list]进来了。
[...]重载解析分两个阶段选择构造函数:
最初,候选函数是类
T
和参数列表的 initializer-list构造函数([dcl.init.list]) 由初始化程序列表作为单个参数组成。[...]
构造函数#2是初始化列表构造器。函数模板参数推导给出
Key = std::array<int>::iterator
所以推论的s2
类型是
std::set<std::array<int>::iterator>
s2
的声明等效于
std::set<std::array<int>::iterator> s2 {a.begin(), a.end()};
因此,s2
是一组迭代器,由两个元素组成:a.begin()
和a.end()
。在您的情况下,std::array<int>::iterator
可能是int*
,并且a.begin()
和a.end()
分别被序列化为0x7ffecf9d12e0
和0x7ffecf9d12f0
。
对于s3
,就像在
__hypothetical __hyp(a.begin(), a.end()); // parentheses
这是直接初始化,并且在[pver.match.ctor]的范围内。 initializer_list
构造函数无关紧要,而是选择了构造函数#1。函数模板参数推导给出
InputIterator = std::array<int>::iterator
所以推论的s3
类型是
set<iterator_traits<std::array<int>::iterator>::value_type>
哪个是set<int>
。因此,s3
的声明等效于
std::set<int> s3 (a.begin(), a.end());
s3
是一组int
,它们是从[a.begin(), a.end())
范围(四个元素1, 2, 3, 4
)开始初始化的,它解释了输出。