ユーザ定義変換
クラス型から別の型への暗黙の変換または明示的な変換を可能にします。
[編集] 構文
変換関数は、以下の形式の名前を持つ、明示的な戻り値の型を持たない、引数なしの非静的メンバ関数またはメンバ関数テンプレートのように宣言されます。
operator conversion-type-id
|
(1) | ||||||||
explicit operator conversion-type-id
|
(2) | (C++11以上) | |||||||
explicit ( expression ) operator conversion-type-id
|
(3) | (C++20以上) | |||||||
conversion-type-id は、 type-id ですが、宣言子に関数および配列演算子 []
または ()
は使用できません (そのため、配列へのポインタなどの型への変換には、型エイリアスや typedef、または恒等テンプレートが必要です。 下を参照してください)。 typedef にかかわらず、 conversion-type-id は配列または関数型を表すことはできません。
ユーザ定義関数の宣言では戻り値の型は使用できませんが、宣言の文法の decl-specifier-seq は存在してもよく、 type-specifier またはキーワード static
以外のいかなる指定子を含んでも構いません。 特に、 explicit
に加えて、指定子 inline
、 virtual
、 constexpr
(C++11以上)、 consteval
(C++20以上)、および friend
も使用できます (friend
は修飾された名前 (friend A::operator B();
) を要求することに注意してください)。
クラス X でそのようなメンバ関数が宣言されると、それは X から conversion-type-id への変換を行います。
struct X { // 暗黙の変換。 operator int() const { return 7; } // 明示的な変���。 explicit operator int*() const { return nullptr; } // エラー、 conversion-type-id では配列演算子は使用できません。 // operator int(*)[3]() const { return nullptr; } using arr_t = int[3]; operator arr_t*() const { return nullptr; } // typedef を通せば OK です。 // operator arr_t () const; // エラー、いかなる場合も配列への変換は許されません。 }; int main() { X x; int n = static_cast<int>(x); // OK、 n を 7 に設定します。 int m = x; // OK、 m を 7 に設定します。 int* p = static_cast<int*>(x); // OK、 p をヌルに設定します。 // int* q = x; // エラー、暗黙の変換はありません。 int (*pa)[3] = x; // OK。 }
[編集] 説明
ユーザ定義変換は暗黙の変換の第2ステージで呼ばれます (暗黙の変換は0~1個の変換コンストラクタまたは0~1個のユーザ定義変換関数から構成されます)。
何らかのユーザ定義変換を行うために変換関数と変換コンストラクタがどちらも使用できる場合は、コピー初期化および参照初期化の文脈では変換関数とコンストラクタの両方がオーバーロード解決によって考慮されますが、直接初期化の文脈ではコンストラクタのみが考慮されます。
struct To { To() = default; To(const struct From&) {} // 変換コンストラクタ。 }; struct From { operator To() const {return To();} // 変換関数。 }; int main() { From f; To t1(f); // 直接初期化。 コンストラクタを呼びます。 // (もし変換コンストラクタが使用できなければ、暗黙のコピーコンストラクタが選択され、 // その引数を準備するために変換関数が呼ばれることに注意してください)。 To t2 = f; // コピー初期化。 曖昧です。 // (もし変換関数が非 const 型から、例えば From::operator To(); であれば、 // このケースではコンストラクタの代わりにそれが選択されることに注意してください。) To t3 = static_cast<To>(f); // 直接初期化。 コンストラクタを呼びます。 const To& r = f; // 参照初期化。 曖昧です。 }
自クラス (または cv 修飾された自クラス) (またはその参照)、自クラスの基底 (またはその参照)、および void 型への変換は、定義することはできますが、仮想ディスパッチを通した一部のケースを除いて、変換シーケンスの一部として実行することはできません。
struct D; struct B { virtual operator D() = 0; }; struct D : B { operator D() override { return D(); } }; int main() { D obj; D obj2 = obj; // D::operator D() を呼びません。 B& br = obj; D obj3 = br; // 仮想ディスパッチを通して D::operator D() を呼びます。 }
メンバ関数呼び出しの構文を用いて呼ぶこともできます。
struct B {}; struct X : B { operator B&() { return *this; }; }; int main() { X x; B& b1 = x; // X::operatorB&() を呼びません。 B& b2 = static_cast<B&>(x); // X::operatorB&() を呼びません。 B& b3 = x.operator B&(); // X::operatorB&() を呼びます。 }
変換関数への明示的な呼び出しを行うとき、 type-id は貪欲です。 つまり、有効な型識別子 (もしあれば属性も含みます) であるトークンの最も長い可能な並びになります。
& x.operator int * a; // & (x.operator int) * a のようにではなく、 // & (x.operator int*) a のように解析されます。
conversion-type-id でプレースホルダ auto を使用することができ、戻り値の型を推定することを表します。 struct X { operator int(); // OK。 operator auto() -> short; // エラー、後置戻り値型は構文の一部ではありません。 operator auto() const { return 10; } // OK、戻り値の型を推定します。 }; ノート: 変換関数テンプレートでは戻り値の型を推定することはできません。 |
(C++14以上) |
変換関数は、継承でき、仮想にできますが、静的にはできません。 派生クラスの変換関数は、同じ型への変換でない限り、基底クラスの変換関数を隠蔽しません。
変換関数はテンプレートメンバ関数にできます。 例えば std::auto_ptr<T>::operator auto_ptr<Y> がそうです。 適用される特別なルールについてはメンバテンプレートおよびテンプレートの実引数推定を参照してください。