为啥在通过 const 引用传递临时值时调用复制构造函数?

Posted

技术标签:

【中文标题】为啥在通过 const 引用传递临时值时调用复制构造函数?【英文标题】:why copy constructor is called when passing temporary by const reference?为什么在通过 const 引用传递临时值时调用复制构造函数? 【发布时间】:2011-06-11 15:09:53 【问题描述】:

我将一个未命名的临时对象传递给使用 const ref 参数定义的函数。类的复制 ctor 是私有的,我得到一个编译错误。我不明白为什么在这种情况下调用复制构造函数。

class A 
public:
  A(int i) 
private:
  A(const A&) 
;

void f(const A& a)



int main()

  f(A(1)); // <-- error here: 'A::A(const A&)' is private

不出所料,当我将 main 更改为:

A a(1);
f(a);

它有效。

编辑:编译器是 gcc 4.1.2

【问题讨论】:

你使用的是哪个编译器? VC++9 很乐意编译第一个变体。 @sharptooth: gcc,我会更新 @davka:gcc?哪个版本? gcc-4.3.4 编译得很好:ideone.com/7sZ17 Unexpected const reference behavior的可能重复 【参考方案1】:

表达式A(1) 是一个右值 5.2.3 [expr.type.conv]。

在使用 rvalue 表达式初始化const 引用(函数参数)时,编译器可能创建一个临时值并将该表达式的值复制到临时并将该引用绑定到该临时。 8.5.3 [dcl.init.ref] / 5.

[...] 无论复制是否实际完成,用于制作复制的构造函数都应是可调用的。

请注意,此行为是由于下一版 C++ 中的更改所致。在新标准中,从类 prvalue 初始化的 const 引用必须直接绑定到引用对象;在这种情况下不允许创建临时对象,并且不使用或不需要复制构造函数。

【讨论】:

谢谢。根据@Nawaz 的评论,您的最后一段解释了为什么在较新版本的 gcc 中不会发生这种情况,对吗? @davka:我相信这就是原因,是的。【参考方案2】:

您可以在Copy Constructor Needed with temp object找到您问题的答案或直接前往http://gcc.gnu.org/bugs/#cxx%5Frvalbind

C++ 标准规定临时 应在此创建对象 上下文及其内容填充 我们正在尝试的对象的副本 绑定到引用;它还说 可以省略临时副本, 但语义约束(例如。 复制构造函数的可访问性) 还是要查的。

如需更多信息,您可以 请参阅以下段落 C++ 标准:[dcl.init.ref]/5, 项目符号 2、子项目符号 1 和 [class.temporary]/2.

从 GCC 4.3.0 开始,GCC 不再 给出了这种情况下的错误。这 改变是基于意图的 C++ 语言委员会。作为 2010-05-28,最终拟定稿 C++0x 标准允许这样做 代码没有错误。

【讨论】:

【参考方案3】:

因为a(1)调用了构造函数A(int i),然后在调用void f(const A&)时又调用了A(const A&)。

使 A(int i) 构造函数显式,你不应该遇到这个错误。

编辑:我想我误解了这个问题。我可能会删除它。

【讨论】:

来自 C++ 标准 §3.2 第 2 段: 即使调用实际上被实现省略,也会使用复制构造函数。 复制构造函数在概念上不应该在这里被调用,这就是问题的重点。通过引用传递意味着没有创建新对象,因此没有副本。 @dalle:您引用的内容是针对类似 RVO 的情况,而不是针对问题中的情况。 @dalle:不,它没有被使用——对象是通过引用传递的,而不是通过值传递的。如果对象仅按值传递,则将使用它,然后可能会发生类似 RVO 的复制省略,并且您引用的部分会启动。 @dalle:我现在明白了,在那个 cse 中它可以被使用甚至调用,但我从没想过会有这样的行为。

以上是关于为啥在通过 const 引用传递临时值时调用复制构造函数?的主要内容,如果未能解决你的问题,请参考以下文章

为啥向量被视为按值传递,即使它是通过 const 引用传递的?

传递常量引用与创建临时常量副本相同,那么为啥要传递常量引用呢? [复制]

复制赋值运算符应该通过 const 引用还是按值传递?

为啥当我们有 const 引用时创建临时对象?

为啥对按值传递的参数使用 const? [复制]

如果 Swift 通过值而不是引用传递,为啥我可以通过将 UIView 传递给函数来在 Swift 中操作 UIView 的属性? [复制]