【问题标题】:Class brace initialisation is misinterpreted for an std::initializer_list instead of copy construction类大括号初始化被误解为 std::initializer_list 而不是复制构造
【发布时间】:2019-08-28 09:00:06
【问题描述】:

考虑以下 C++11 代码:

#include <initializer_list>

struct MyStruct {
    MyStruct() {}
    MyStruct(const MyStruct& other) {}
    void doStuff() const {}
};

int main() {
    MyStruct a;
    auto b{a};

    a.doStuff();
    b.doStuff();
    return 0;
}

我期待 bMyStruct 的一个实例,从 a 复制构造,但是,当使用 GCC 4.9.1 编译时,bstd::initializer&lt;MyStruct&gt;。 GCC 8.2 按预期编译。

注意我在 Godbolt 上做了一个例子:https://godbolt.org/z/adNDoO

您能解释一下这两个编译器版本之间的区别吗? (或者标准对此有何规定?)

【问题讨论】:

标签: c++ c++11 gcc


【解决方案1】:

这是在 C++14 中修复的 C++11 中的错误。看起来 GCC 8.2 正在考虑 braced-init-list (N3922) 的新规则,即使您使用 C++ 11 标志进行编译。

新角色说:

对于直接列表初始化:

  • 对于只有一个元素的花括号初始化列表,自动推导将推导 从那个条目;
  • 对于包含多个元素的花括号初始化列表,自动推导格式错误。

在你的情况下:

MyStruct a;
auto b{a};

它遵循第一条规则,这就是它编译没有问题的原因。

老一代的 GCC 4.9.1 没有实现这些新规则,所以默认情况下它认为它是 std::initializer_list

【讨论】:

  • 有趣的怪癖!我想我们可以称之为怪癖:D 谢谢你的澄清!
【解决方案2】:

这是自 C++ 11 以来的预期行为:

在以下情况下会自动构造 std::initializer_list 对象: ... 一个花括号初始化列表绑定到自动,包括在一个范围内的 for 循环中

因此,当您将 {x1, x2, ..., xn} 分配给“自动”时,如果所有值都具有相同的类型,您将获得类型为 std::initializer_list&lt;decltype(x1)&gt; 的对象(为简单起见,我在此处跳过引用和 cv):

auto a = {5};   // std::initializer_list<int>
auto b {5};     // std::initializer_list<int>
auto c = {1, 2}; // std::initializer_list<int>
auto d {1, 2};   // std::initializer_list<int>

但是在 C++17 中,这发生了变化。添加了以下规则:

  • 对于副本列表初始化自动推演会推演一个 std::initializer_list 如果列表中的所有元素都相同 键入,或格式错误。
  • 对于直接列表初始化,如果列表只有一个元素,自动推导将推导出一个 T,如果有超过 一个元素。

所以当你使用符合 C++17 的编译器进行编译时,你会得到

auto a = {42};   // std::initializer_list<int>
auto b {42};     // int
auto c = {1, 2}; // std::initializer_list<int>
auto d {1, 2};   // error, too many

我相信 gcc 8.2 不能针对这种情况正确处理 -std=c++11

【讨论】:

    猜你喜欢
    • 2016-12-20
    • 2023-03-05
    • 2015-09-04
    • 2018-01-13
    • 2016-12-22
    • 2013-09-16
    • 1970-01-01
    • 2015-01-24
    • 1970-01-01
    相关资源
    最近更新 更多