8

問題を説明する最小限のコード例を次に示します。

#include <iostream>

class Thing
{
   // Non-copyable
   Thing(const Thing&);
   Thing& operator=(const Thing&);

   int n_;

public:
   Thing(int n) : n_(n) {}

   int getValue() const { return n_;}
};

void show(const Thing& t)
{
   std::cout << t.getValue() << std::endl;
}

int main()
{
   show(3);
}

これにより、同じエラーが発生します。

int main()
{
    show( Thing(3) );
}

AIX下のIBMXLC / C ++ 8.0コンパイラーは、以下の警告を出します。

"testWarning.cpp", line 24.9: 1540-0306 (W) The "private" copy constructor "Thing(const Thing &)" cannot be accessed.
"testWarning.cpp", line 24.9: 1540-0308 (I) The semantics specify that a temporary object must be constructed.
"testWarning.cpp", line 24.9: 1540-0309 (I) The temporary is not constructed, but the copy constructor must be accessible.

また、「-Wall」と「-pedantic」を使用してg ++ 4.1.2を試しましたが、診断が得られませんでした。ここでコピーコンストラクタへのアクセスが必要なのはなぜですか?オブジェクトをコピー可能にする(これは私の制御の範囲外です)か、渡すための明示的なコピーを作成する(実際のオブジェクトのコピーに費用がかかる場合)以外に、警告を取り除くにはどうすればよいですか?

4

4 に答える 4

9

このルールは、標準の §8.5.3/5 にあります。識別された 3 つの基本的な状況があります。1つ目は、初期化子(あなたの場合は「3」)が左辺値であるか、クラス型を持つことを含みます。どちらも当てはまらないため、3 番目のケースは、クラス型を持たない右辺値で const 参照を初期化することです。このケースは、8.5.3/5 の最後の箇条書きでカバーされています。

それ以外の場合は、「cv1 T1」型のテンポラリが作成され、非参照コピー初期化 (8.5) の規則を使用して初期化子式から初期化されます。その後、参照は一時にバインドされます。T1 が T2 に関連する参照である場合、cv1 は cv2 と同じ cv 修飾であるか、cv2 より大きい cv 修飾でなければなりません。そうでない場合、プログラムの形式が正しくありません。

編集:読み直して、IBMが正しいと思います。以前は一時をコピーする必要がある可能性を考えていましたが、それが問題の原因ではありません。§8.5 で指定されている非参照コピー初期化を使用して一時を作成するには、コピー ctor が必要です。特に、この時点では、次のような式と同等です。

T x = a;

これは基本的に次と同等です。

T x = T(a);

つまり、テンポラリを作成してから、そのテンポラリを初期化中のオブジェクトにコピーする必要があります (この場合、これテンポラリです)。必要なプロセスを要約すると、次のようなコードとほぼ同じです。

T temp1(3);
T temp2(temp1); // requires copy ctor
show(temp2);    // show's reference parameter binds directly to temp2
于 2009-10-23T20:50:44.883 に答える
3

C++ では、十分にスマートなコンパイラが一時オブジェクトのコピーを回避できます。これは、標準で許可されているas-ifルールの違反の 1 つです。私は IBM の AIX C++ コンパイラに詳しくありませんが、show(3)呼び出しには一時的な Thing をコピーする必要があると考えているようです。その場合、C++ では、コンパイラがそれを使用しないほどスマートであっても、アクセス可能なコピー コンストラクターが必要です。

しかし、show(3)そもそもなぜコピーが必要なのでしょうか? 私が理解できないこと。運が良ければ、litb もすぐに使えるようになります。

于 2009-10-23T20:26:02.483 に答える
1

私の直感では、ジェリーの答えは正しいと思いますが、まだいくつか質問があります。

興味深いのは、そのセクションの前の段落 ( 391 ) をカバーする核となる問題があることです。この問題は、引数が同じクラス型である場合に関連しています。具体的には:

int main () {
  show ( Thing (3) );       // not allowed under current wording
                            // but allowed with Core Issue 391

  show ( 3 );               // Still illegal with 391
}

Core Issue 391の変更は、一時的な右辺値が同じクラス タイプを持つ場合にのみ影響します。以前の文言には次のものがありました。

初期化子式が右辺値であり、T2 がクラス型であり、参照cv1 T1との参照互換性がある場合cv2 T2,、次のようにバインドされます。

[...]

コピーを作成するために使用されるコンストラクターは、コピーが実際に行われたかどうかにかかわらず呼び出し可能でなければなりません。

その最後の行はshow(Thing(3))、現在の標準に従って違法になるものです。このセクションの提案された文言は次のとおりです。

初期化式が右辺値であり、T2 がクラス型であり、「cv1 T1」が「cv2 T2」と参照互換性がある場合、参照は右辺値によって表されるオブジェクトにバインドされます (3.10 [basic.lval] を参照)。そのオブジェクト内のサブオブジェクトに。

この時点で、g++ は391に従って動作を更新した可能性があると考えましたが、変更には誤ってコピー初期化のケースが含まれていました。ただし、これは、私がテストした g++ のバージョンでは実証されていません。

class A{
public:
  A ();
  A (int);
private:
  A (A const &);
};

void foo (A const &);

void foo ()
{
  A a = 3 ;     // 3.2.3 (ERROR), 3.4.6(ERROR), 4.4.0(ERROR), Comeau(ERROR)
  
  foo ( 3 ) ;   // 3.2.3 (OK), 3.4.6(OK), 4.4.0(OK), Comeau(OK)
  foo ( A() );  // 3.2.3 (OK), 3.4.6(ERROR), 4.4.0(OK), Comeau(OK)
  foo ( A(3) ); // 3.2.3 (OK), 3.4.6(ERROR), 4.4.0(OK), Comeau(OK)
}

この場合の Jerry の解釈にfoo (3)間違いはありませんが、コンパイラの動作が異なるため、疑問があります。

于 2009-10-26T13:15:36.837 に答える
0

一時的なものに名前を付けようとするとどうなりますか?

Thing temp(3);
show(temp);

于 2009-10-23T20:41:41.970 に答える