为什么使用统一的初始化程序语法会导致与“旧的”初始化行为不同的行为?样式()? [英] Why does using uniform initializer syntax result in different behavior to the "old" style ()?

查看:82
本文介绍了为什么使用统一的初始化程序语法会导致与“旧的”初始化行为不同的行为?样式()?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果尝试对 std :: set 使用统一的初始化程序,则会得到不同的结果。

I get different results if I try to use a uniform initializer for 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  

这似乎与扣除指南有关,如果与 {} ()语法。

This seems to be related to "deduction guides", which are evaluated differently if used with {} or () syntax.

推荐答案

简短答案



对于 s2 ,使用大括号语法,而 {a.begin(),a.end()} 被视为< std :: array< int> :: iterator 的code> initializer_list 。因此, s2 是一组迭代器。

Short answer

For s2, brace syntax is used, and {a.begin(), a.end()} is considered to be an initializer_list of std::array<int>::iterators. Therefore, s2 is a set of iterators.

对于 s3 ,使用括号语法,并选择迭代器构造函数。 s3 是一组 int s,并从范围内初始化。 (),a.end())

For s3, parentheses syntax is used, and the iterator constructor is selected. s3 is a set of ints, and is initialized from the range [a.begin(), a.end()).

[set.overview] ,我们这里有两个相关的推导指南:

Per [set.overview], we have two deduction guides relevant here:


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>;                                                







每< a href = https://timsong-cpp.github.io/cppwp/n4659/over.match.class.deduct#1 rel = noreferrer> [over.match.class.deduct] / 1 :


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

A set of functions and function templates is formed comprising:


  • [...]

  • [...]

(1.4)对于每个推导指南,具有以下属性的函数或函数模板:

(1.4) For each deduction-guide, a function or function template with the following properties:


  • 模板参数(如果有)和函数参数是扣除指南

返回类型是推导指南中的 simple-template-id

在这种情况下,前述推导指南的综合函数和函数模板分别为

In this case, the synthesized functions and function templates for the aforementioned deduction guides are, respectively,

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>; 

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

(I used double underscores to signify that these names are synthesized, and are not otherwise accessible.)

每个 [over.match.class.deduct] / 2


初始化和重载解析按照
[dcl.init]和[over.match.ctor],[over.match.copy]或
[over.match.list]中的描述进行(如适合于假设类类型的对象执行的初始化
的类型),其中出于以下目的,选择的
函数和函数模板被视为该类类型的
构造函数形成一个重载
集,并由执行类
模板自变量推断的上下文提供初始化程序。如果函数或函数
模板是从构造函数或 deduction-guide 生成
声明为<$ c $的函数或函数
模板,则认为每个这样的概念性
构造函数都是显式的c>明确。所有这些概念性构造函数都被视为
是假设类类型的公共成员。

Initialization and overload resolution are performed as described in [dcl.init] and [over.match.ctor], [over.match.copy], or [over.match.list] (as appropriate for the type of initialization performed) for an object of a hypothetical class type, where the selected functions and function templates are considered to be the constructors of that class type for the purpose of forming an overload set, and the initializer is provided by the context in which class template argument deduction was performed. Each such notional constructor is considered to be explicit if the function or function template was generated from a constructor or deduction-guide that was declared explicit. All such notional constructors are considered to be public members of the hypothetical class type.

假设类类型如下:

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] 进来。


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

[...] overload resolution selects the constructor in two phases:


  • 最初,候选函数是< T 类的em> initializer-list构造函数([dcl.init.list])和参数列表
    包含以下初始化列表:

  • Initially, the candidate functions are the initializer-list constructors ([dcl.init.list]) of the class T and the argument list consists of the initializer list as a single argument.

[...]

构造函数#2是初始化列表构造器。函数模板参数推导给出

Constructor #2 is an initializer-list constructor. Function template argument deduction gives

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

Therefore, s2 is a set of iterators that consists of two elements: a.begin() and a.end(). In your case, std::array<int>::iterator is probably int*, and a.begin() and a.end() happen to be serialized as 0x7ffecf9d12e0 and 0x7ffecf9d12f0, respectively.

对于 s3 ,执行重载解析就像在

For s3, overload resolution is performed as if in

__hypothetical __hyp(a.begin(), a.end()); // parentheses

这是直接初始化,并且在 [pver.match.ctor] initializer_list 构造函数无关紧要,而是选择了构造函数#1。函数模板参数推导给出

That's direct-initialization, and is under the scope of [pver.match.ctor]. The initializer_list constructor is irrelevant, and the Constructor #1 is selected instead. Function template argument deduction gives

InputIterator = std::array<int>::iterator

所以 s3 的推导类型为

set<iterator_traits<std::array<int>::iterator>::value_type>

哪个是 set< int> 。因此, s3 的声明等效于

Which is set<int>. Therefore, the declaration of s3 is equivalent to

std::set<int> s3 (a.begin(), a.end());

s3 是一组<$ c从范围 [a.begin(),a.end())初始化的$ c> int s—四个元素 1、2、3、4 ,用于解释输出。

s3 is a set of ints that is initialized from the range [a.begin(), a.end()) — four elements 1, 2, 3, 4, which explains the output.

这篇关于为什么使用统一的初始化程序语法会导致与“旧的”初始化行为不同的行为?样式()?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆