10
class A
{
 public:
  A(const int n_);
  A(const A& that_);
  A& operator=(const A& that_);
};

A::A(const int n_)
{ cout << "A::A(int), n_=" << n_ << endl; }

A::A(const A& that_)    // This is line 21
{ cout << "A::A(const A&)" << endl; }

A& A::operator=(const A& that_)
{ cout << "A::operator=(const A&)" << endl; }

int foo(const A& a_)
{ return 20; }

int main()
{
  A a(foo(A(10)));    // This is line 38
  return 0;
}

执行此代码给出 o/p:

A::A(int), n_=10
A::A(int), n_=20

显然复制构造函数永远不会被调用。

class A
{
 public:
  A(const int n_);
  A& operator=(const A& that_);
 private:
  A(const A& that_);
};

但是,如果我们将其设为私有,则会发生此编译错误:

Test.cpp:在函数'int main()'中:
Test.cpp:21:错误:'A :: A(const A&)'是私有
Test.cpp:38:错误:在此上下文中

为什么编译器在实际不使用复制构造函数时会抱怨?
我正在使用 gcc 版本 4.1.2 20070925 (Red Hat 4.1.2-33)

4

8 回答 8

12

核心缺陷 391解释了该问题。

基本上,当前的 C++ 标准在将类类型的临时值传递给 const 引用时需要一个可用的复制构造函数。

此要求将在 C++0x 中删除。

需要复制构造函数背后的逻辑来自这种情况:

C f();
const C& r = f(); // a copy is generated for r to refer to
于 2009-05-28T10:05:56.507 回答
5

2003 年标准在 §12.2/1 中规定:

即使避免创建临时对象(12.8),也必须遵守所有语义限制,就像创建临时对象一样。[示例:即使不调用复制构造函数,也应满足所有语义限制,例如可访问性(第 11 条)。]

周围也有类似的例子。据我所知,编译器可以自由生成临时文件或优化它们。

于 2009-05-28T09:26:41.750 回答
3

据我所知,您没有在任何地方使用复制构造函数。在语句中foo(A(10)),您将创建一个 A 类的临时对象并将其作为 const 引用传递给 foo。foo 返回一个用于构造 object 的整数a。因此,我看不到复制构造函数在哪里涉及以及 NRVO 是如何出现的。此外,我通过将复制构造函数设为私有来编译以下代码,并且在 VS2008 中编译良好。

using namespace std;

class A
{
 public:
  A(const int n_);
 private:
  A(const A& that_);
  A& operator=(const A& that_);
};

A::A(const int n_)
{ cout << "A::A(int), n_=" << n_ << endl; }

A::A(const A& that_)    // This is line 21
{ cout << "A::A(const A&)" << endl; }

A& A::operator=(const A& that_)
{ 
    cout << "A::operator=(const A&)" << endl; 
    return *this;
}

int foo(const A& a_)
{ return 20; }


int main(int argc,char *argv[])
{
   A a(foo(A(10)));    // This is line 38
  return 0;

}   
于 2009-05-28T09:13:22.293 回答
2

再说一句:编译器在使用临时文件时会做不同的事情。所以这不是关于复制构造函数,而是关于中间临时的。

A original(10);
foo( original ); // does compile
foo( A(10) ); // doesn't compile - needs a copy constructor
于 2009-05-28T09:54:29.013 回答
1

不使用复制构造函数,但为了使代码编译复制构造函数需要可访问。

编辑:Comeau C++ 编译器报告如下:

Comeau C/C++ 4.3.10.1 (Oct  6 2008 11:28:09) for ONLINE_EVALUATION_BETA2
Copyright 1988-2008 Comeau Computing.  All rights reserved.
MODE:strict errors C++ noC++0x_extensions

"ComeauTest.c", line 38: error: "A::A(const A &)" (declared at line 17), required
          for copy that was eliminated, is inaccessible
    A a(foo(A(10)));    // This is line 38
            ^

1 error detected in the compilation of "ComeauTest.c".

请注意,如果启用了 C++0x 扩展,它可以在 Comeau C++ 编译器中正常编译。

于 2009-05-28T07:59:46.607 回答
1

在表达式中:

A a(foo(A(10)));

子表达式的结果是类型A(10)右值A。(5.2.3 [expr.type.conv])

当从右值初始化 const 引用时,编译器可能会从右值创建一个临时并将其绑定到该引用。即使它选择不这样做,复制构造函数也必须是可访问的。(8.5.3 [decl.init.ref]) 如果引用是从强制直接绑定的引用兼容 左值初始化的,则不会出现这种情况。

由于foo通过引用而不是值获取其参数,因此参数初始化本身没有强制要求的副本。

foo返回一个 int,所以这里没有 an 的副本A

a是直接从 foo 返回的 int 初始化的,所以这里没有副本A

于 2009-05-28T10:24:41.307 回答
0

通常,您不必担心是否以及何时调用复制构造函数。C++ 标准对于何时删除或添加对复制构造函数的调用非常宽松。如果您的班级在逻辑上需要它,则提供它(并且不要忘记析构函数和赋值运算符)是明智的规则。

于 2009-05-28T09:26:14.343 回答
0

调用时:

foo( A(10) );

在调用期间正在创建一个临时对象。正在使用复制构造函数来填充数据。临时对象在调用执行后被移除。

调用时:

{ 
  A original(10);
  foo( original ); 
}

退出区块后,原件将被丢弃。它可以安全地用作参数。

为了获得最佳速度,通过引用传递对象,使用编译器在优化期间将丢弃的临时变量。

于 2009-05-28T10:12:47.463 回答