名前空間
変種
操作

ユーザ定義変換

提供: cppreference.com
< cpp‎ | language
 
 
C++言語
一般的なトピック
フロー制御
条件付き実行文
繰り返し文 (ループ)
ジャンプ文
関数
関数宣言
ラムダ関数宣言
inline 指定子
例外指定 (C++20未満)
noexcept 指定子 (C++11)
例外
名前空間
指定子
decltype (C++11)
auto (C++11)
alignas (C++11)
記憶域期間指定子
初期化
代替表現
リテラル
ブーリアン - 整数 - 浮動小数点
文字 - 文字列 - nullptr (C++11)
ユーザ定義 (C++11)
ユーティリティ
属性 (C++11)
typedef 宣言
型エイリアス宣言 (C++11)
キャスト
暗黙の変換 - 明示的な変換
static_cast - dynamic_cast
const_cast - reinterpret_cast
メモリ確保
クラス
クラス固有の関数特性
特別なメンバ関数
テンプレート
その他
 
一般
値カテゴリ (lvalue, rvalue, xvalue)
評価順序 (副作用完了点)
定数式
未評価式
一次式
ラムダ式(C++11)
リテラル
整数リテラル
浮動小数点リテラル
ブーリアンリテラル
文字リテラル および エスケープシーケンス
文字列リテラル
ヌルポインタリテラル(C++11)
ユーザ定義リテラル(C++11)
演算子
代入演算子: a=b, a+=b, a-=b, a*=b, a/=b, a%=b, a&=b, a|=b, a^=b, a<<=b, a>>=b
インクリメントとデクリメント: ++a, --a, a++, a--
算術演算子: +a, -a, a+b, a-b, a*b, a/b, a%b, ~a, a&b, a|b, a^b, a<<b, a>>b
論理演算子: a||b, a&&b, !a
比較演算子: a==b, a!=b, a<b, a>b, a<=b, a>=b, a<=>b(C++20)
メンバアクセス演算子: a[b], *a, &a, a->b, a.b, a->*b, a.*b
その他の演算子: a(...), a,b, a?b:c
デフォルト比較(C++20)
演算子の代替表現
優先順位と結合性
畳み込み式(C++17)
new 式
delete 式
throw 式
alignof
sizeof
sizeof...(C++11)
typeid
noexcept(C++11)
演算子オーバーロード
変換
暗黙の変換
const_cast
static_cast
reinterpret_cast
dynamic_cast
明示的な変換 (T)a, T(a)
ユーザ定義変換
 

クラス型から別の型への暗黙の変換または明示的な変換を可能にします。

[編集] 構文

変換関数は、以下の形式の名前を持つ、明示的な戻り値の型を持たない、引数なしの非静的メンバ関数またはメンバ関数テンプレートのように宣言されます。

operator conversion-type-id (1)
explicit operator conversion-type-id (2) (C++11以上)
1) すべての暗黙の変換および明示的な変換に参加するユーザ定義変換関数を宣言します。
2) 直接初期化および明示的な変換にのみ参加するユーザ定義変換関数を宣言します。

conversion-type-id は、 type-id ですが、宣言子に関数および配列演算子 [] または () は使用できません (そのため、配列へのポインタなどの型への変換には、型エイリアスや typedef、または恒等テンプレートが必要です。 下を参照してください)。 typedef にかかわらず、 conversion-type-id は配列または関数型を表すことはできません。

ユーザ定義関数の宣言では戻り値の型は使用できませんが、宣言の文法decl-specifier-seq は存在してもよく、 type-specifier またはキーワード static 以外のいかなる指定子を含んでも構いません。 特に、 explicit に加えて、指定子 inlinevirtualconstexpr、および 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> がそうです。 適用される特別なルールについてはメンバテンプレートおよびテンプレートの実引数推定を参照してください。