【问题标题】:Empty braces magic in initializer lists初始化列表中的空括号魔术
【发布时间】:2021-03-25 16:05:39
【问题描述】:

考虑以下最小示例:

#include <iostream>

struct X {
  X() { std::cout << "Default-ctor" << std::endl; }
  X(std::initializer_list<int> l) { 
      std::cout << "Ilist-ctor: " << l.size() << std::endl; 
  }
};

int main() {
    X a{};
    X b({}); // reads as construct from {}
    X c{{}}; // reads as construct from {0}
    X d{{{}}}; // reads as construct from what?
    // X e{{{{}}}}; // fails as expected
}

Godbolt example

我对a、b和c没有任何疑问,一切都很清楚

但我不明白为什么 d 有效

d 中这对额外的大括号代表什么?我查了 C++20 标准,但我不能轻易找到答案。 clang 和 gcc 都同意这个代码,所以是我错过了什么

【问题讨论】:

  • 我不确定,但它可能是通过内括号初始化的另一个临时对象的复制/移动(当然,由于 C++17 的原因,它实际上不会是复制/移动规则)。
  • @chris 我用 C++14 和 -fno-elide-constructors 运行它,并且没有调用任何构造函数。

标签: c++ c++20 initializer-list


【解决方案1】:

获取有关编译器功能的信息的一个很好的技巧是使用所有错误进行编译: -Weverything。让我们看看这里的输出(仅限d):

9.cpp:16:6: warning: constructor call from initializer list is incompatible with C++98                                                                                            
      [-Wc++98-compat]                                                                                                                                                            
  X d{{{}}}; // reads as construct from what?                                                                                                                                     
     ^~~~~~                           

X::X(std::initializer_list) 被调用。

9.cpp:16:8: warning: scalar initialized from empty initializer list is incompatible with                                                                                          
      C++98 [-Wc++98-compat]                                                                                                                                                      
  X d{{{}}}; // reads as construct from what?                                                                                                                                     
       ^~                               

标量 (int) 在内部 {} 中初始化。所以我们有X d{{0}}

9.cpp:16:7: warning: initialization of initializer_list object is incompatible with                                                                                               
      C++98 [-Wc++98-compat]                                                                                                                                                      
  X d{{{}}}; // reads as construct from what?                                                                                                                                     
      ^~~~                                                                                                                                                                        
5 warnings generated.                                                                                                                                                             

std::initializer_list{0} 初始化。所以我们有X d{std::initializer_list&lt;int&gt;{0}};

这向我们展示了我们需要的一切。额外的括号用于构造初始化列表。

注意:如果您想添加额外的括号,您可以通过调用复制/移动构造函数(或省略它),但 C++ 编译器不会为您隐式执行此操作以防止错误:

X d{X{{{}}}}; // OK
X e{{{{}}}}; // ERROR

【讨论】:

    【解决方案2】:

    我只是想说明一下:

    X d{               {                       {}        }};
       |               |                       |
       construct an    |                       |
       `X` from ...    an initializer_list     |
                       containing...           int{}
    

    列表初始化的规则是找到一个initializer_list&lt;T&gt; 构造函数并尽可能使用它,否则......枚举构造函数并做正常的事情。

    使用X{{}},即列表初始化:最外层的{}s 是initializer_list,其中包含一个元素:{},即0。足够直截了当(虽然很神秘)。

    但是对于X{{{}}},使用最外层的{} 作为initializer_list 将不再有效,因为您无法从{{}} 初始化int。所以我们回退到使用构造函数。现在,其中一个构造函数接受了initializer_list,所以这有点像重新开始,只是我们已经剥掉了一层大括号。


    这就是为什么例如vector&lt;int&gt;{{1, 2, 3}} 也可以工作,而不仅仅是vector&lt;int&gt;{1, 2, 3}。但就像......不要。

    【讨论】:

    • 谢谢,我已经接受了第一个答案,但这也是很好的解释。顺便说一句,我发现了更有趣的大括号疯狂示例: std::vector<:string> vs = {{{{{}}}}};在这里,按照您的解释字符串是由 {{}} 和来自 {{s}} 的向量构造的,具有统一初始化 {v},哇...
    猜你喜欢
    • 1970-01-01
    • 2012-11-21
    • 1970-01-01
    • 2016-12-11
    • 2019-03-12
    • 2018-12-12
    • 1970-01-01
    • 2020-05-14
    • 2013-08-23
    相关资源
    最近更新 更多