为什么复制ctor在此代码中使用?

时间:2009-05-28 07:55:00

标签: c++ copy-constructor

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&amp;)'是私人的   Test.cpp:38:错误:在此上下文中

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

8 个答案:

答案 0 :(得分:12)

Core defect 391解释了这个问题。

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

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

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

C f();
const C& r = f(); // a copy is generated for r to refer to

答案 1 :(得分:5)

2003年标准,§12.2/ 1,声明:

  

即使创作了   避免使用临时对象(12.8),   所有的语义限制必须是   尊重好像是临时对象   创建了。 [例子:即使   所有都没有调用复制构造函数   语义限制,如   无障碍(第11条),应为   满意。 ]

周围有类似的例子。从我收集的内容来看,编译器可以自由地生成临时值或优化它们。

答案 2 :(得分:3)

到目前为止,我看到你没有在任何地方使用复制构造函数。在语句foo(A(10))中,您正在创建A类的临时对象,并将其作为const引用传递给foo。 foo返回一个整数,用于构造对象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;

}   

答案 3 :(得分:2)

另一个评论:编译器在处理临时工时做了不同的事情。所以它不是关于复制构造函数,而是关于中间临时的。

A original(10);
foo( original ); // does compile
foo( A(10) ); // doesn't compile - needs a copy constructor

答案 4 :(得分: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 ++编译器中进行编译。

答案 5 :(得分:1)

在表达式中:

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

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

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

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

foo返回一个int,因此此处没有A的副本。

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

答案 6 :(得分:0)

通常,您不应该担心是否以及何时调用复制构造函数。关于何时删除对复制构造函数的调用或添加的内容,C ++标准非常宽松。如果你的类在逻辑上需要它,那么提供它(并且不要忘记析构函数和赋值运算符)是合理的规则。

答案 7 :(得分:0)

致电:

foo( A(10) );

在呼叫的生命周期内正在创建临时对象。正在使用复制构造函数来填充数据。执行调用后将删除临时对象。

致电:

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

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

为获得最佳速度,请使用临时变量按引用传递对象,编译器在优化过程中将丢弃该临时变量。