C++の変換演算子がどのように機能するかを理解してください。ここに私が理解しようとしている簡単な例がありますが、変換が実際にコンパイラによってどのように行われるかはあまり明確ではありません。
class Example{
public:
Example();
Example(int val);
operator unsigned int();
~Example(){}
private:
int itsVal;
};
Example::Example():itsVal(0){}
Example::Example(int val):itsVal(val){}
Example::operator unsigned int (){
return (itsVal);
}
int main(){
int theInt = 5;
Example exObject = theInt; // here
Example ctr(5);
int theInt1 = ctr; // here
return 0;
}
デバッガーでそのコードをウォークスルーして(および/または各コンストラクターと演算子にブレークポイントを設定して)、どのコンストラクターと演算子がどの行によって呼び出されているかを確認できます。
それらを明示的に定義しなかったため、コンパイラーは、クラスの非表示/デフォルトのコピーコンストラクターと代入演算子も作成しました。デバッガーを使用して、どこで/いつ呼び出されているかを確認する場合は、これらを明示的に(次のように)定義できます。
Example::Example(const Example& rhs)
: itsVal(rhs.itsVal)
{}
Example& operator=(const Example& rhs)
{
if (this != &rhs)
{
this->itsVal = rhs.itsVal;
}
return *this;
}
_int main() {
int theInt = 5;
/**
* Constructor "Example(int val)" in effect at the statement below.
* Same as "Example exObject(theInt);" or "Example exObject = Example(theInt);"
*/
Example exObject = theInt; // 1
Example ctr(5);
/**
* "operator unsigned int()" in effect at the statement below.
* What gets assigned is the value returned by "operator unsigned int()".
*/
int theInt1 = ctr; // 2
return 0;
}
_
ステートメント1で、コンストラクタExample(int val)
が呼び出されます。これをexplicit Example(int val)
として宣言すると、コンパイル時エラーが発生します。つまり、このコンストラクタでは暗黙的な変換は許可されません。
割り当てられた値がそれぞれの引数タイプである場合、すべての単一引数コンストラクターは暗黙的に呼び出されます。単一引数のコンストラクタの前にexplicit
キーワードを使用すると、暗黙的なコンストラクタの呼び出しが無効になるため、暗黙的な変換が無効になります。
コンストラクタが明示的に宣言された場合、つまりexplicit Example(int val)
の場合、各ステートメントで次のことが起こります。
_Example exObject(theInt); // Compile time error.
Example exObject = theInt; // Compile time error.
Example exObject(Example(theInt)); // Okay!
Example exObject = Example(theInt); // Okay!
_
また、暗黙的なコンストラクター呼び出し、したがって暗黙的な変換の場合、割り当てられた値は右辺値、つまり、左辺値(theInt)を使用して暗黙的に作成された名前のないオブジェクトであり、暗黙的な変換の場合にコンパイラーが変換することを通知します。
_Example exObject = theInt;
_
に
_Example exObject = Example(theInt);
_
したがって、(C++ 11では)左辺値、つまり名前付き値theInt
を割り当てに使用しているので、左辺値コンストラクターが呼び出されることを期待しないでください。割り当てられる値は実際には左辺値を使用して作成された名前のないオブジェクトであるため、呼び出されるのは右辺値コンストラクタです。ただし、これは、コンストラクターの左辺値と右辺値の両方のバージョンがある場合に適用されます。
ステートメント2でoperator unsigned int()
が呼び出されます。それを単に奇妙な名前の通常の関数呼び出しと見なし、暗黙的な変換が発生したときに自動的に呼び出されるという事実を考えてください。その関数によって返される値は、式で割り当てられた値です。また、実装では返される値がintであるため、正しく_int theInt1
_に割り当てられます。
正確にはoperator unsigned int()
は、キャスト演算子である_()
_演算子をオーバーロードします。あなたの場合、それはint
に対してオーバーロードされるため、Example
クラスのオブジェクトがint
に割り当てられるときは常に、Example
からint
が実行されるため、operator unsigned int()
が呼び出されます。したがって、
_int theInt1 = ctr;
_
に相当
_int theInt1 = (int)ctr;
_
Example exObject = theInt; // implicitly created copy constructor takes place
// object implicitly created from int and then copied
// it is like
Example exObject = Example(theInt);
// so it uses sequence
// Example(int) -> Example(const Example&)
int theInt1 = ctr; // operator int()
コンパイラがコピーコンストラクタの最適化と戻り値の最適化をサポートしている場合は、気付かないでしょう
Example(const Example&)
実行しますが、コピーコンストラクタをプライベートに宣言して、私が話していることを理解することができます。
Example exObject = theInt; // here
これは、intをExampleに暗黙的に変換し、intを受け入れる非明示的なコンストラクターによって影響を受けます。
コンパイラーがインスタンスのコピーを省略できる場合でも、これには例のコピーコンストラクターの可用性も必要です。
int theInt1 = ctr; // here
これは、キャスト演算子によって提供される、Exampleからunsigned intへの暗黙の変換を使用します。
キャスト演算子は、コードがわかりにくくなる傾向があるため、通常は避けられます。単一引数のコンストラクターを明示的にマークして、クラス型への暗黙的な変換を無効にすることができます。 C++ 0xは、変換演算子を明示的にマークする可能性も追加する必要があります(したがって、それらを呼び出すにはstatic_castが必要ですか?-私のコンパイラーはそれらをサポートしておらず、すべてのWebリソースはブールへの明示的な変換に集中しているようです)。