【问题标题】:Clang vs GCC vs MSVC - using brace-enclosed initializer list to create user defined type?Clang vs GCC vs MSVC - 使用大括号括起来的初始化列表来创建用户定义的类型?
【发布时间】:2014-11-29 09:12:50
【问题描述】:

正在尝试用大括号括起来的初始化程序列表,编译器的行为似乎有所不同(嗯,这是一个相当新的功能),但想知道正确的行为应该是什么,特别是如果下面提到的“c3”实际上应该使用私有编译,默认初始化变量?

int main() {
    struct c1 {
        bool b;
        char c;
        int i;
    };

    auto c1_ = c1{true, '0', 0};
    //clang-3.6 - okay
    //gcc-4.9   - okay
    //vs-2013   - okay

    // ------------------------------------------

    struct c2 {
        bool b;
        char c;
        int i = 0; // with default value
    };

    auto c2_ = c2{true, '0', 0};
    //clang-3.6 - okay
    //gcc-4.9   - error: no matching function for call to ‘c2::c2(<brace-enclosed initializer list>)’
    //vs-2013   - error C2440: '<function-style-cast>' : cannot convert from 'initializer-list' to 'c2'

    auto c2__ = c2{true, '0'};
    //clang-3.6 - okay
    //gcc-4.9   - error: no matching function for call to ‘c2::c2(<brace-enclosed initializer list>)’
    //vs-2013   - error C2440: '<function-style-cast>' : cannot convert from 'initializer-list' to 'c2'

    // ------------------------------------------

    struct c3 {
        bool b;
        char c;
    private:
        int i = 0; // with private default value
    };

    auto c3_ = c3{true, '0'};
    //clang-3.6 - error: no matching constructor for initialization of 'c3'
    //gcc-4.9   - error: no matching function for call to ‘c3::c3(<brace-enclosed initializer list>)’
    //vs-2013   - error C2440: '<function-style-cast>' : cannot convert from 'initializer-list' to 'c3'
}

【问题讨论】:

    标签: c++ visual-c++ gcc c++11 clang


    【解决方案1】:
     auto c2_ = c2{true, '0', 0};
     auto c2__ = c2{true, '0'};
    

    这些在 C++14 中都很好。类内初始化不会阻止类型成为 C++14 中的聚合,当大括号初始化器未提供值时,将使用类内值。

    auto c3_ = c3{true, '0'};
    

    这是一个错误,因为聚合不能有 private:protected: 成员。由于它不是聚合,因此您只有隐式声明的默认构造函数。您可以执行以下操作:

    auto c3_ = c3{};
    

    【讨论】:

      【解决方案2】:

      c2 不是 C++11 中的聚合,而是 C++14 中的聚合。所以代码不应该在前者中编译,而应该在后者中。看起来 clang 是在 C++14 模式下编译的。

      c3 不是聚合,因为存在 private 数据成员,因此无论标准如何,它都不应编译..

      【讨论】:

        【解决方案3】:

        这是聚合初始化。

        定义聚合的 C++11 段落说 (n3337, §8.5.1/1):

        聚合是没有用户提供的数组或类(第 9 条) 构造函数 (12.1),没有大括号或等号 非静态数据的初始化器 成员(9.2),没有私有或受保护的非静态数据成员(条款 11),没有基类(第 10 条),也没有虚函数(10.3)。

        这意味着c2 不是聚合,auto c2_ = c2{true, '0', 0}; 应该在 C++11 中触发诊断(gcc 是正确的)。

        但是,使用 clang,您似乎在 C++14 模式下编译,这消除了“无大括号或等号初始化程序”限制(n3690,§8.5.1/1):

        聚合是没有用户提供的数组或类(第 9 条) 构造函数(12.1),没有私有或受保护的非静态数据成员 (第 11 条),没有基类(第 10 条),没有虚函数 (10.3)。

        所以,auto c2_ = c2{true, '0', 0}; 在 C++14 中是可以的。

        关于auto c2__ = c2{true, '0'};,§8.5.1/7 (n3690) 说:

        如果列表中的 initializer-clauses 少于 聚合中的成员,然后每个成员未显式初始化 应从其 brace-or-equal-initializer 初始化,或者,如果有 不是 brace-or-equal-initializer,来自一个空的初始化列表 (8.5.4)。

        也就是说没问题,clang 用 C++14 模式编译是对的。

        c3 在 C++11 和 C++14 中都不是聚合(因为私有成员),所以所有编译器拒绝它都是正确的。

        【讨论】:

        • “但是,clang 似乎在这里应用了 C++14” 他正在以 C++14 模式编译。如果 clang 设置为 C++11 模式,它会为 c2 初始化程序产生错误。
        • 是的,示例是在 c++14 模式下编译的,似乎 gcc 还没有实现 c++14 规则,无论如何将聚合限制为私有成员的动机是什么?似乎私有初始化成员会很好,不是吗?
        • @Kris 我不能以任何权威说,但聚合的概念可以追溯到 C,其中不存在访问说明符。此外,C++ 不保证具有不同访问级别的成员之间的相对布局,因此允许此类类型的聚合初始化将使其非常不可移植。
        猜你喜欢
        • 2016-12-11
        • 2012-11-21
        • 1970-01-01
        • 1970-01-01
        • 2013-08-23
        • 2011-05-31
        • 1970-01-01
        • 2016-04-14
        • 1970-01-01
        相关资源
        最近更新 更多