IBM XL C / C ++コンパイラからのこの警告はなぜですか?
-
06-07-2019 - |
質問
問題を説明する最小限のコード例を次に示します。
#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でのIBM XL C / 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.
&quot; -Wall&quot;でg ++ 4.1.2も試しました。および「-pedantic」診断はありませんでした。ここでコピーコンストラクターへのアクセスが必要なのはなぜですか?オブジェクトをコピー可能にする(コントロール外)か、明示的なコピーを作成して渡す(実際のオブジェクトをコピーするのに費用がかかる場合)以外に、警告を削除するにはどうすればよいですか?
解決
このルールは、標準の&#167; 8.5.3 / 5にあります。特定された3つの基本的な状況があります。最初は、左辺値であるか、クラス型を持つイニシャライザ(あなたの場合は '3')を含みます。どちらも当てはまらないため、3つ目のケースがあります。クラス型を持たない右辺値でconst参照を初期化する場合です。このケースは、8.5.3 / 5の最後の箇条書きでカバーされています。
それ以外の場合、タイプ&#8220; cv1 T1&#8221;の一時非参照コピー初期化の規則を使用して、初期化式から作成および初期化されます(8.5)。その後、参照は一時にバインドされます。 T1がT2に参照関連である場合、cv1はcv2と同じcv-qualification、またはそれより大きいcv-qualificationでなければなりません。それ以外の場合、プログラムは不正な形式です。
編集:再読、IBMはそれを正しく持っていると思う。以前は一時をコピーする必要があると考えていましたが、それが問題の原因ではありません。 &#167; 8.5で指定されている非参照コピーの初期化を使用して一時ファイルを作成するには、コピーアクターが必要です。特に、この時点では、次のような式と同等です:
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
他のヒント
C ++は、標準で許可されている as-if 規則に違反する一時オブジェクトのコピーを回避するための十分にスマートなコンパイラを許可します。私はIBMのAIX C ++コンパイラに精通していませんが、 show(3)
の呼び出しには一時的なものをコピーする必要があると考えているようです。その場合、C ++では、コンパイラーが賢くても使用できないコピーコンストラクターが必要です。
しかし、最初に show(3)
でコピーが必要なのはなぜですか?私が理解できないこと。運が良ければ、litbは少しは一緒になります。
私の直感は、ジェリーの答えは正しいですが、まだいくつかの質問があります。
興味深いのは、そのセクションの前の段落( 391 )。その問題は、引数が同じクラス型である場合に関係します。具体的には:
int main () {
show ( Thing (3) ); // not allowed under current wording
// but allowed with Core Issue 391
show ( 3 ); // Still illegal with 391
}
主要な問題の変更 391 一時的な右辺値が同じクラス型を持っている場合にのみ影響します。前の文言は次のとおりでした:
初期化式が右辺値で、T2がクラス型であり、
cv1 T1
がcv2 T2
と参照互換の場合、参照は次のようにバインドされます:[...]
コピーの作成に使用されるコンストラクターは、コピーが実際に行われたかどうかに関係なく呼び出し可能です。
その最後の行は、現在の標準に従って show(Thing(3))
を違法にするものです。このセクションで提案されている文言は次のとおりです。
初期化式が右辺値であり、T2がクラス型で、「cv1 T1」が「cv2 T2」と参照互換性がある場合、参照は右辺値(3.10 [basic.lval]を参照)で表されるオブジェクトまたはそのオブジェクト内のサブオブジェクトにバインドされます。
この時点で、 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)
}
foo(3)
の場合のJerryの解釈に誤りはありませんが、コンパイラーの動作が異なるため疑問があります。
一時的なものに名前を付けようとするとどうなりますか?
Thing temp(3);
show(temp);