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.220070925(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

もう 1 つ注意してください。コンパイラは、テンポラリを操作するときに別のことを行います。したがって、コピーコンストラクターではなく、中間の一時的なものです。

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)));

サブ式の結果はtypeA(10)右辺値Aです。(5.2.3 [expr.type.conv])

右辺値から const 参照を初期化するとき、コンパイラは右辺から一時を作成し、それを参照にバインドする場合があります。そうしないことを選択した場合でも、コピー コンストラクターにアクセスできる必要があります。(8.5.3 [decl.init.ref]) これは、直接バインディングが義務付けられている参照互換の 左辺値から参照が初期化されている場合には当てはまりません。

foo値ではなく参照によってパラメーターを取るため、引数の初期化自体に必須のコピーはありません。

fooint を返すため、ここに an のコピーはありませんA

afoo によって返される 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 に答える