何时应使用直接初始化,何时应使用复制初始化?

21

这只是偏好还是有特定情况下需要使用其中一种变体?我指的是以下几种初始化方式:

T t(e); // direct initialization
T t = e; // copy initialization

1
什么是隐式赋值?你是指默认的赋值运算符吗? - Yakov Galka
1
“隐式赋值”是什么意思? - Cheers and hth. - Alf
2
@Mike:这两个都不是赋值,它们都是初始化。 - Stuart Golodetz
1
@Mike:这些都不是赋值语句。你刚刚编造了这个术语。它们是复制初始化,因此它们是相同的。 - Yakov Galka
2
我已经修正了你的帖子,使其更接近你想要询问的内容。 - Johannes Schaub - litb
显示剩余2条评论
2个回答

23
你描述的实际名称不是隐式和显式赋值,而是:
  • 拷贝初始化: T x = a;
  • 直接初始化: T x(a);
它们不相同,尤其是在需要转换的情况下,例如当T为类类型而a为不同类型时(请参见Alf评论中不涉及转换的上下文示例)。考虑以下代码:
class Test
{
public:
    explicit Test(int i) { /* ... */ }
};

int main()
{
    Test t(0);  // OK : calls Test::Test(int)
    Test u = 0; // KO : constructor is marked explicit
}

引述标准(8.5/14)的意思是:

  • 对于直接初始化和复制初始化,如果源类型与目标类型相同或是派生类,则会考虑构造函数
  • 对于其他复制初始化情况,例如我示例中main函数的第二行,会考虑用户自定义转换序列。由于使用explicit关键字禁止了对Test构造函数进行隐式转换,因此第二行代码无法编译。

在需要进行转换的情况下,它们并不等价 -- 还有其他情况下它们也不等价。 - Cheers and hth. - Alf
@Alf:我写这句话时不太确定,但是我找不到任何例子。我可能在术语上有些错误,因为我在你的回答中看到的确实是一种转换(通过标准所谓的“转换构造函数”),实际上与我的回答非常相似(除了花括号部分我没有涉及)。我错过了什么? - icecrime
1
好的,假设a是表达式T()。 :-)那么复制初始化的语法形式大体上等同于默认初始化(除了可能涉及临时和复制),而直接初始化的语法形式将解释为函数声明,“最令人烦恼的解析”。此外,请考虑显式复制构造函数的情况。根据允许的模糊程度,请考虑花括号初始化程序。因此,至少有3种情况。但直接的反例是“显式”复制构造函数。干杯! - Cheers and hth. - Alf
@Alf:我明白了 :) 我绝对想不到这些! - icecrime

11

直接初始化就像这样

std::istringstream  stream( "blah blah" );

当所涉及的类型(此处为C++标准库中的std::istringstream)没有可访问的复制构造函数时,需要使用move初始化

像这样进行复制初始化

std::istringstream  stream = "blah blah";   //! NOT VALID

需要一个可访问的复制构造函数,因为它被执行时,似乎在等号右侧创建了一个临时对象,然后该临时对象用于初始化正在声明的变量。

在另一方面,在C++98中需要使用复制初始化语法才能使用花括号初始化程序。例如,直接初始化无法用于初始化聚合体。但是,您可以使用带有花括号初始化程序的复制初始化:

#include <string>
using namespace std;

struct Answer
{
    int     nVotes;
    string  description;
};    

int main()
{
    Answer const  incorrect   = { 26, "they're the same!" };
    Answer const  correct     = { -1, "nah, they're different, actually" };
}

因此,存在显著差异。

通常我更喜欢使用复制初始化语法,因为它更加清晰。但有时候,就像上面展示的那样,不幸的是,直接初始化是必要的。一些人,例如C++教材作者Francis Glassborow,已经将直接初始化作为他们首选的初始化语法(我不确定为什么,对我来说不太清晰,并且引入了“最棘手的解析”问题),对于他们来说,在某些情况下需要复制初始化是不幸的。

祝好!


3
这篇答案是在2010年写的。使用C++11的复制初始化"语法"可以在更多情况下使用,因为它转化为了C++11的移动语义。此外,直接初始化的语法已经扩展到包括花括号值列表。 - Cheers and hth. - Alf

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