什么是两者之间的区别?
T a(b);
并且
T a = b;
并且
T a = T(b);
?
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
。
b
的类型为T
时,编译器不仅可以自由省略临时复制,而且必须这样做。但是仍然需要一个可访问的复制构造函数。此外,复制初始化要求一个可访问的复制构造函数,该构造函数不是“explicit”。 - Cheers and hth. - AlfT a = b
的情况下,右手边从b
构造了一个临时对象,然后从该临时对象中复制构造了a
... 我进行了测试,创建了带有所有类型构造函数的类,并在调用T a = b
时只进行了一次复制构造... 那么我在这里错过了什么吗? - LaithT a(b);
T a = b;
创建一个临时的T
类型对象,由b
进行构造。然后调用复制构造函数(此处=
不是赋值,在下一个情况中也是如此!)。
T a = T(b);
与上面相同!只是我们明确构造了一个临时对象。
请注意,标准允许在第二和第三种情况下完全消除临时副本。此外,如果b
不是类型T
,则在第一种情况下,T
不必具有复制构造函数。在第二和第三种情况下,即使实现可以优化整个过程,仍需要可访问的复制构造函数。我IRC标准称之为:复制省略。
b
类型相关的非复制构造函数(如果使用)可以是显式的。而在第二种情况下不行。不确定您是否指的是“除了我们显式地构造了一个临时对象”这一点。 - Steve Jessopexplicit
构造函数的情况下不起作用。 - Khaled AlshayaT a(b);
中,假设 b
的类型没有命中任何接受 b
类型的构造函数,但是 b
的类型中存在一些转换运算符,那么该转换将被考虑吗? - Mr.AnubisT a(b);
调用的是 T
的构造函数,而不是 a
。 - starriet
T a = T(b)
的相关信息:什么是复制省略和返回值优化? - jrh