【问题标题】:Initialization difference with or without Curly braces in C++11C++11 中带或不带花括号的初始化差异
【发布时间】:2014-02-04 16:02:30
【问题描述】:

我们可以在 C++11 中通过两种方式初始化变量

一个:

int abc = 7;

两个:

int abc {7};

这两种方法有什么区别?

编译器如何区别对待它们或执行这些代码的方式?

【问题讨论】:

  • 对于int,唯一的区别是{}禁止缩小转换,例如来自太大而无法放入int 的文字。对于类类型,它可能会变得复杂,具体取决于您想了解差异的详细程度。

标签: c++11 initialization initializer-list


【解决方案1】:

虽然对于 int 的现有回复是完整的,但我痛苦地发现,在某些情况下,(){} 初始化之间存在其他差异。

关键字是{}是一个初始化列表。

这样一种情况是,std::string 初始化与 count char 的副本:

std::string stars(5, '*')

stars初始化为*****,但是

std::string stars{5, '*'}

将被读取为std::string stars(char(5), '*'),并将星号初始化为*(前面有一个隐藏字符)。

【讨论】:

    【解决方案2】:

    短版

    通过{..} 初始化是列表初始化,它禁止缩小转换。例如,如果LLONG_MAXlong long int 的最大值,而您的int 不能表示:

    int x = LLONG_MAX;  // probably accepted with a warning
    int x {LLONG_MAX};  // error
    

    同样:

    long long y = /*something*/;
    
    int x = y;  // accepted, maybe with a warning
    int x {y};  // error
    

    加长版

    表单的初始化

    T x = a;
    

    复制初始化;任一形式的初始化

    T x(a);
    T x{a};
    

    直接初始化,[dcl.init]/15-16。

    [dcl.init]/14 然后说:

    初始化的形式(使用括号或=)通常是无关紧要的,但是当初始化器或被初始化的实体具有类类型时就很重要;见下文。

    所以对于非类类型,初始化的形式 无关紧要。但是,这两种直接初始化是有区别的:

    T x(a);  // 1
    T x{a};  // 2
    

    同样,在这两个复制初始化之间:

    T x = a;    // 1
    T x = {a};  // 2
    

    {..} 使用列表初始化。 {..} 被称为 braced-init-list

    因此,当您比较 T x = a;T x {a}; 时,有 两个 区别:复制与直接初始化,以及“非列表”与列表初始化。正如其他人在上面的引用中已经提到的那样,对于非类类型T,复制初始化和直接初始化没有区别。但是,list-init 和没有 list-init 之间是有区别的。也就是说,我们也可以比较

    int x (a);
    int x {a};
    

    在这种情况下,列表初始化禁止缩小转换。在 [dcl.init.list]/7 中将缩小转换定义为:

    窄化转换是隐式转换

    • 从浮点类型到整数类型,或者

    • long doubledoublefloat,或从doublefloat,除非源是常量表达式并且转换后的实际值在可以表示 (即使不能准确表示),或者

    • 从整数类型或无作用域枚举类型到浮点类型,除非源 是一个常量表达式,转换后的实际值将适合目标类型并且将 转换回原始类型时产生原始值,或

    • 从整数类型或无作用域枚举类型到不能代表所有的整数类型 原始类型的值,除非源是一个常量表达式,其值在整数之后 促销活动将适合目标类型。

    【讨论】:

    • 这种同时使用括号和大括号的初始化怎么样:std::random_device{}()
    • @moooeeeep 这不是一种单独的初始化。它使用表达式std::random_device{} 构造一个std::random_device 类型的临时对象,然后调用该对象的重载operator(),就像std::random_device rd; rd() 那样。 random_device 有一个 operator(),它调用 RNG 并返回一个(伪)随机数,参见 en.cppreference.com/w/cpp/numeric/random/random_device/…
    • 很好,谢谢!既然你解释了它,它似乎很明显。
    • 我尝试使用 int b1{2147483648} 进行初始化; .但我没有收到错误,而是只收到警告“警告:将'2147483648ll'从'long long int'缩小到'int' inside {} [-Wnarrowing]|。为什么会这样?
    • @Rajesh 哪个编译器和版本?显然,这只是在 gcc 5 之前的警告。另请参阅:gcc.gnu.org/bugzilla/show_bug.cgi?id=55783
    【解决方案3】:

    第一个是复制初始化,第二个是列表初始化。

    但是,通常复制初始化很少使用。因为,如果您通过传递用户定义类型的对象来执行此操作,它只会导致位复制,因此如果用户定义的类使用指针,则可能不会产生预期的结果。

    【讨论】:

    • 如果他们有复制构造函数,肯定不会?我现在很困惑。
    • @RichieHH 如果用户定义的类型有指针,那么应该更喜欢编写复制构造函数以及构造函数和析构函数(规则 3)。但是,如果没有复制构造函数,那么它将导致“浅复制”并可能导致悬空指针。
    • 没错。使用指针的用户定义类型应该具有复制和初始化构造函数。也许编辑您的回复。感谢您回来。
    猜你喜欢
    • 2021-07-09
    • 2013-03-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多