初始化:括号 vs. 等号

50

什么是两者之间的区别?

T a(b);

并且

T a = b;

并且

T a = T(b);

?


关于 T a = T(b) 的相关信息:什么是复制省略和返回值优化? - jrh
2个回答

41
T a( b );

如果解析为函数声明,那么这就是一个函数声明,否则它是直接初始化

T a = b;

复制初始化,这意味着它的工作方式就像在右侧构造了一个临时对象,然后从该临时对象中复制构造或移动构造a,其中后一种情况只适用于C++11及以后的版本。

编译器可以在任何时候消除(删除)临时对象和复制/移动,但是无论使用哪个逻辑,必须保持可访问和非explicit的复制或移动构造函数。

例如,在C++03中,您不能使用复制初始化来复制初始化std::ostringstream,因为它没有复制构造函数。在C++11中,如果初始化程序是临时值,则可以对ostringstream进行复制初始化,然后得到一个逻辑移动构造(通常会被消除优化)。例如,以下是一个复制初始化声明:

ostringstream s = ostringstream( "blah" );

在C++03中,…无法编译,因为在C++03中,复制初始化会调用类的复制构造函数,而该构造函数不存在。然而,在C++11中,复制初始化会调用移动构造函数,因此可以编译成功。虽然(为了保持其作为流的幻象)std::ostringstream不能直接复制,但它可以被移动。

另一个这样的区别是:在C++03中,只有复制初始化语法支持花括号初始化,当T是聚合类型(如原始数组)时,可以使用花括号初始化。在C++11中,大括号符号已经扩展和广泛化为一种统一的初始化语法,因此也可以用于直接初始化。因此,以下直接初始化声明:

int v[]{ 3, 1, 4, 1, 5, 9, 2, 6, 5, 4 };

这段代码在C++03下无法编译,但是在C++11及之后的版本可以编译。

=复制初始化语法是从C语言继承而来的原始初始化语法。

由于移动语义的存在,在C++11及之后的版本中,它可以被用于更广泛的情况,比如std::ostringstream


FredOverflow指出,当b的类型为T时,编译器不仅可以自由省略临时复制,而且必须这样做。但是仍然需要一个可访问的复制构造函数。此外,复制初始化要求一个可访问的复制构造函数,该构造函数不是“explicit”。 - Cheers and hth. - Alf
我知道这是一个老问题,但是你提到在T a = b的情况下,右手边从b构造了一个临时对象,然后从该临时对象中复制构造了a ... 我进行了测试,创建了带有所有类型构造函数的类,并在调用T a = b时只进行了一次复制构造... 那么我在这里错过了什么吗? - Laith
1
@Laith:嗯,你在读的那个句子中错过了“好像”,在接下来的那个句子中是“编译器可以自由删除(省略)临时对象+复制”。;-)请注意,即使复制构造函数具有副作用,编译器也可以自由省略复制构造。 实质上,编译器可以盲目地假定复制构造函数只是复制,而不考虑明显的现实。 另外,自从2010年写下这个答案以来,我们已经有了C++11,通过引入移动语义改变了一些东西。 C++复制初始化可以移动或复制,具体取决于情况。 - Cheers and hth. - Alf
谢谢,那解除了我的困惑。 - Laith
1
谢谢你提醒我这个旧答案。我已经更新了它,希望没有引入任何严重错误。 :) - Cheers and hth. - Alf
显示剩余3条评论

24
T a(b);

调用一个接受b为参数的a构造函数。(如果b是同一类型,则调用复制构造函数)。
T a = b;

创建一个临时的T类型对象,由b进行构造。然后调用复制构造函数(此处=不是赋值,在下一个情况中也是如此!)。

T a = T(b);

与上面相同!只是我们明确构造了一个临时对象。

请注意,标准允许在第二和第三种情况下完全消除临时副本。此外,如果b不是类型T,则在第一种情况下,T不必具有复制构造函数。在第二和第三种情况下,即使实现可以优化整个过程,仍需要可访问的复制构造函数。我IRC标准称之为:复制省略。


3
第二种情况和第三种情况的实际区别在于,在第三种情况下,与 b 类型相关的非复制构造函数(如果使用)可以是显式的。而在第二种情况下不行。不确定您是否指的是“除了我们显式地构造了一个临时对象”这一点。 - Steve Jessop
好的观点。也许我应该像你指出的那样澄清,第二个在explicit构造函数的情况下不起作用。 - Khaled Alshaya
T a(b); 中,假设 b 的类型没有命中任何接受 b 类型的构造函数,但是 b 的类型中存在一些转换运算符,那么该转换将被考虑吗? - Mr.Anubis
2
第一行有错别字:T a(b); 调用的是 T 的构造函数,而不是 a - starriet

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接