为什么会调用复制构造函数而不是转换构造函数?

22

基本上,这段代码:

class A {
};
class B { 
   B (const B& b) {}
public: 
   B (){}
   B (const A& a) {} 
};

int main()
{
   A a;
   B b1(a);  //OK
   B b2 = a; //Error
}

只有B b2 = a会产生错误,错误信息如下:

error: ‘B::B(const B&)’ is private

为什么会尝试调用复制构造函数以及直接转换构造函数?

从错误信息可以看出,先创建了一个临时的B实例,然后将其用于复制构造,但是为什么会这样呢?这在标准中有说明吗?


那么明确一下,您是在问为什么编译器这样做(即您想要复制初始化定义的标准参考),还是在问为什么标准这样做(即您想要该定义的动机)? - Steve Jessop
1
@SteveJessop 我首先要求参考资料,但了解动机也很好(不会发布新问题,因为它可能会被删除)。 - Luchian Grigore
@SteveJessop 很公正。我认为Jesse对Als的回答的评论是一个可能的解释。 - Luchian Grigore
@Luchian:我同意这是一种可能性,尽管我认为它的推论可以被反驳。每种内置类型都可以轻松地进行复制,因此我认为仅具有转换并不能完全模拟语义——对于内置类型,直接初始化和复制初始化的语义是相同的。但它确实缺少了一些东西。我想“动机”问题的问题在于人们在回答中展示了他们的工作和推论的过程,而他们没有直接的来源! - Steve Jessop
@SteveJessop发布了新问题https://dev59.com/f2gu5IYBdhLWcg3wln8O - Luchian Grigore
显示剩余2条评论
1个回答

14
B b2 = a;

这被称为复制初始化,它执行以下操作:

  1. 通过使用 B (const A& a)a 创建类型为 B 的对象。
  2. 通过使用 B (const B& b) 将创建的临时对象复制到 b2
  3. 通过使用 ~B() 销毁临时对象。

你得到的错误不是在步骤1而是在步骤2。

标准中的位置在哪里?

C++03 8.5 初始化
第14段:

....
— 如果目标类型是(可能带有cv限定符的)类类型:
...
...
— 对于剩余的复制初始化情况,将枚举可将源类型转换为目标类型或(使用转换函数时)其派生类的用户定义转换序列,如13.3.1.4中所述,并通过重载决议(13.3)选择最佳转换。如果无法进行转换或存在歧义,则初始化无效。选择的函数将使用初始化表达式作为其参数调用;如果函数是一个构造函数,则调用将初始化目标类型的临时对象。调用的结果(对于构造函数情况而言是临时对象)然后用于根据上述规则直接初始化作为复制初始化目标的对象。在某些情况下,实现允许通过将中间结果直接构造到正在初始化的对象中来消除此直接初始化中固有的复制;请参见12.2, 12.8。


1
但是为什么不直接使用转换构造函数呢? - Luchian Grigore
@LuchianGrigore:确实如此。错误发生在完成转换后,在复制构造期间。对于编译器,调用复制构造函数也可以省略。但是,复制构造函数仍然需要可访问性。 - Alok Save
谢谢!我想问一下这背后的动机是什么,但那只会被关闭。 - Luchian Grigore
1
@LuchianGrigore:我认为原因是更接近内置类型的语义。例如,int i = 1.0;:这里发生了两件事情。首先将 1.0 转换为 int,然后将值分配给 i,只有一个转换运算符无法模拟语义(我认为)。 - Jesse Good
@JesseGood 请看 https://dev59.com/f2gu5IYBdhLWcg3wln8O - Luchian Grigore
显示剩余2条评论

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