7

如果我尝试对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  

这似乎与“演绎指南”有关,如果与{}or()语法一起使用,则其评估方式不同。

4

1 回答 1

7

简短的回答

For s2, 使用大括号语法,并且{a.begin(), a.end()}被认为是initializer_lists的一个std::array<int>::iterator。因此,s2是一组迭代器。

对于s3,使用括号语法,并选择迭代器构造函数。s3ints 的集合,并从范围 中初始化[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>;                                                

[over.match.class.deduct]/1

形成一组函数和函数模板,包括:

  • [...]

  • (1.4) 对于每个deduction-guide,具有以下属性的函数或函数模板:

    • 模板参数(如果有)和函数参数是deduction-guide的参数。

    • 返回类型是 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>; 

(我使用双下划线表示这些名称是合成的,否则无法访问。)


[over.match.class.deduct]/2

按照 [dcl.init] 和 [over.match.ctor]、[over.match.copy] 或 [over.match.list] 中的描述执行初始化和重载解析(根据执行的初始化类型)假设类类型的对象,其中选定的函数和函数模板被认为是该类类型的构造函数,以形成重载集,并且初始化器由执行类模板参数推导的上下文提供. 如果函数或函数模板是从已声明的构造函数或演绎指南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]进来了。

[...] 重载决议分两个阶段选择构造函数:

  • 最初,候选函数是类的初始化列表构造函数([dcl.init.list]),T参数列表由初始化列表作为单个参数组成。

  • [...]

构造函数 #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>::iteratoris 可能int*, and a.begin()anda.end()碰巧分别被序列化为0x7ffecf9d12e0and 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从范围初始化的 s [a.begin(), a.end())- 四个元素1, 2, 3, 4,它解释了输出。

于 2019-05-26T11:42:26.903 回答