独自のキャスト演算子を実装する - C++ プログラミング
PROGRAM
独自のキャスト演算子を実装する
C++ のクラスでは、キャスト演算子をオーバーロードして、任意のデータ型へ変換する処理を自分で実装できます。
独自キャストの実装
ヘッダーファイルでは、キャストしたいデータ型(たとえば int 型)を operator キーワードを使って定義します。
class CMyClass
{
public:
operator int() const;
}
キャスト演算子の場合は、戻り値の型はそのキャストした型になるのは明らかなので、戻り値の型の指定は不要です。
また、読み取り専用のインスタンスでも使えるように const を付けておく必要があります。
実装部分では普段通り、ヘッダーに定義したものを実装して、適切な値を返すようにします。
CMyClass::operator int() const
{
// キャストした後の型で、適切な値を返すようにします。
return 0;
}
明示キャスト
たとえばこのように int 型のキャスト演算子を定義することで、このクラスを int 型にキャストすることができます。
CMyClass classValue;
functionWithInt((int)classValue);
このように int 型の引数を取る関数 functionWithInt を呼び出す場合でも、クラスのインスタンスを int 型にキャストして簡単に呼び出すことができます。
int 型の変数に値を代入することもできます。
int intValue = classValue;
暗黙キャスト
なお、functionWithInt 関数が引数に int 型しかとらない場合など、クラスの型を int 型として扱うことが明らかなときは、暗黙的にキャストしてくれるので、キャストを省略することもできます。
functionWithInt(classValue);
他にも、たとえば関数の引数や条件文などの bool 型の値をとるところでクラスのインスタンスを使うと、bool 型のキャスト演算子が実装されていない場合でも、bool と互換性のある int 型のキャスト演算子が暗黙的に呼び出されたりもします。
ただし、暗黙的にキャストできる型が 2 つ以上ある場合はコンパイルエラーになります。
たとえばクラスに int 型と bool 型のキャスト演算子が定義されていて、int または bool の引数を取る関数を呼び出すような場合は、適切な型へ明示的にキャストする必要があります。
暗黙キャストを禁止する
予期しない暗黙キャストを避けられるように、C++11 では explicit キーワードが用意されました。
class CMyClass
{
public:
explicit operator const char*() const;
}
このように、キャスト演算子の定義の冒頭で "explicit" を指定することで、この型へのキャストは明示的に行わなくてはいけなくなります。
ただし bool 型に限っては、条件文などの "コンテキストに依存した bool 型への変換" が行われる場所では、explicit キーワードが指定されていても暗黙的に bool 型への変換が行われます。
たとえば、次のようなプログラムで、暗黙的な bool 変換が行われます。
explicit operation bool() const;
// if 条件文で使うと explicit でも bool にキャストされる
if (classValue) { }
// if 条件文で "!" 演算子と使用すると explicit でも bool にキャストされる
if (!classValue) { }
// if 条件文で他の条件と "&&" や "||" で連結した場合も explicit でも bool にキャストされる
if (classValue && i > 0) { }
// for ループの条件式で使うと explicit でも bool にキャストされる
for (CMyClass classValue = CMyClass(); !classValue; classValue.next()) { }
// while ループの条件式で使うと explicit でも bool にキャストされる
while (classValue) { }
// 三項演算子の条件式で使うと explicit でも bool にキャストされる
const char* s = (classValue ? "Y" : "N");
ただし、条件式内であっても次のように、クラスインスタンスを bool 型と等価演算子 "==" を使って比較するような場合には、explicit 指定された bool キャスト演算子は、指定通り、明示的なキャストが必要になります。
explicit operation bool() const;
// explicit された bool 演算子は、条件式でも等価演算子を使って比較する場合は、通常通り、明示キャストが必要
if ((bool)classValue == true)
{
}
ここで特に注意したいのが、explicit 指定されていない(暗黙キャストが許可されている)int 演算子が定義されている場合です。
bool 演算子が explicit 指定されていない場合は bool 型へのキャストが優先されるのですけど、bool 演算子が explicit 指定されている場合は、explicit 指定されていない int へのキャストが暗黙的に行われます。
explicit operation bool() const;
operation int() const;
// bool 演算子があってもそれが explicit されていると、等価演算子では明示キャストが必要
// そのため、bool と互換性があって explicit が指定されていない int キャストがあるとそれで暗黙キャストされる
if (classValue == true)
{
}
// bool が explicit で int が explicit ではない場合は、上の条件式は実際には下と同じになる
if ((int)classValue == true)
{
}
さらに注意したいのが、同じ条件で、等価演算子を使わない場合です。
この場合、条件式では bool キャストに explicit が指定されていても "コンテキストに依存した bool 型への変換" が行われるため、暗黙キャストを使った場合、等価演算子を使った場合とは違って暗黙 bool キャストが行われます。
explicit operation bool() const;
operation int() const;
// bool 演算子が explicit されていても、条件式では暗黙キャストが行われる。
if (classValue)
{
}
// explicit 指定に拠らず bool に暗黙キャストされるため、上の条件式は実際には下と同じになる
if ((bool)classValue)
{
}
クラスの場合、クラスを直接格納する変数と、クラスへのポインタを格納する変数とをごっちゃにしてしまうと、このあたりでプログラムミスをすることもあるかもしれないので気を付けたいところです。
特にクラスへのポインタの場合、条件文でそのポインタを指定すると、それが nullptr なら false に nullptr 以外なら true になるという性質があるので、それと間違えないように注意します。
[ もどる ]