名前空間
変種
操作

暗黙の変換

提供: 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
メモリ確保
クラス
クラス固有の関数特性
特別なメンバ関数
テンプレート
その他
 
 


暗黙の変換は、何らかの型 T1 の式が、その型を受理しないけれども何らかの別の型 T2 を受理する文脈で使用されたときに行われます。 特に、

  • 引数として T2 で宣言された関数を呼ぶときに引数としてその式が使用されたとき。
  • T2 を期待する演算子で被演算子としてその式が使用されたとき。
  • T2 型の新しいオブジェクトを初期化するとき (T2 を返す関数内での return 文を含みます)。
  • switch 文でその式が使用されたとき (T2 は整数型です)。
  • if 文やループでその式が使用されたとき (T2bool です)。

T1 から T2 への曖昧でない暗黙の変換シーケンスがひとつ存在する場合にのみ、プログラムは well-formed です (コンパイルできます)。

呼ばれる関数または演算子のオーバーロードが複数存在する場合は、 T1 から利用可能なそれぞれの T2 への暗黙の変換シーケンスが組み立てられた後、オーバーロード解決のルールにより、どのオーバーロードがコンパイルされるかが決定されます。

ノート: 算術式では、二項演算子の被演算子に対する暗黙の変換のための結果の型は、通常の算術変換という別のルールの集合によって決定されます。

目次

[編集] 変換の順序

暗黙の変換シーケンスは以下の内容から (この順序で) 構成されます。

1) 0個または1個の標準変換シーケンス
2) 0個または1個のユーザ定義変換
3) 0個または1個の標準変換シーケンス

コンストラクタまたはユーザ定義変換関数への引数を考慮するときは、1個の標準変換シーケンスだけが使用できます (そうでなければユーザ定義変換が実質的に連鎖できます)。 ある組み込み型から別の組み込み型に変換するときは、1個の標準変換シーケンスだけが使用できます。

標準変換シーケンスは以下の内容から (この順序で) 構成されます。

1) 0個または1個の左辺値変換
2) 0個または1個の数値昇格または数値変換
3) 0個または1個の関数ポインタ変換
(C++17以上)
4) 0個または1個の修飾子調節

ユーザ定義変換は0個または1個の非 explicit 単一引数コンストラクタまたは非 explicit 変換関数の呼び出しから構成されます。

T2e からコピー初期化できる、つまり、何らかの架空の一時変数 t について宣言 T2 t = e; が well-formed である (コンパイルできる) 場合に限り、式 eT2 に暗黙に変換可能であると言います。 これは直接初期化 (T2 t(e)) と異なることに注意してください。 直接初期化では explicit コンストラクタおよび変換関数が追加で考慮されます。

[編集] 文脈的な変換

以下の文脈では、 bool 型が期待され、宣言 bool t(e); が well-formed であれば (つまり explicit T::operator bool() const; のような explicit 変換関数が考慮されます)、暗黙の変換が行われます。 そのような式 ebool に文脈的に変換されると言います。

  • ifwhilefor の制御式。
  • 組み込みの論理演算子 !&& および || の被演算子。
  • 条件演算子 ?: の第1被演算子。
  • static_assert 宣言の述語。
  • noexcept 指定子の式。
(C++20以上)
(C++11以上)

以下の文脈では、文脈固有の型 T が期待され、クラス型 E の式 e は、 E が許容可能な型への非 explicit ユーザ定義変換関数をひとつ持つ (C++14未満)戻り値の型が T (cv 修飾されていても構いません) または T (cv 修飾されていても構いません) への参照である非 explicit 変換関数を E が持つような型 T が許容可能な型の中でちょうど1個存在し、 eT に暗黙に変換可能である (C++14以上)場合にのみ、使用できます。 そのような式 e は指定された型 T文脈的に暗黙に変換されると言います。 explicit 変換関数は (bool への文脈的な変換とは異なり) 考慮されません。 (C++11以上)

  • delete 式の引数 (T は任意のオブジェクトポインタ型です)。
  • 整数定数式 (リテラルクラスが使用された場合 (T は任意の整数型またはスコープなし列挙型であり、選択されたユーザ定義変換関数は constexpr でなければなりません)。
  • switch 文の制御式 (T は任意の整数型または列挙型です)。
#include <cassert>
 
template<typename T>
class zero_init
{
    T val;
public:
    zero_init() : val(static_cast<T>(0)) { }
    zero_init(T val) : val(val) { }
    operator T&() { return val; }
    operator T() const { return val; }
};
 
int main()
{
    zero_init<int> i; assert(i == 0);
    i = 7; assert(i == 7);
    switch(i) { }     // C++14 未満はエラー (変換関数が2個以上あります)。
                      // C++14 以上は OK (両方の関数が同じ型 int に変換します)。
    switch(i + 0) { } // 常に OK (暗黙の変換)。
}

[編集] 値変換

値変換は式の値カテゴリを変更する変換です。 異なる値カテゴリの式を期待する演算子の被演算子として式が現れたときに行われます。

[編集] 左辺値から右辺値への変換

任意の非関数非配列型 Tglvalue は同じ型の prvalue に暗黙に変換できます。 T が非クラス型の場合、この変換は cv 修飾も削除します。 glvalue が std::nullptr_t 型の場合、結果の prvalue はヌルポインタ定数 nullptr です。

未評価文脈 (sizeof、 typeid、 noexcept または decltype の被演算子) の中で遭遇したのでない限り、この変換は実質的にコンストラクタ引数として元の glvalue を用いて T 型の一時オブジェクトをコピー構築し、その一時オブジェクトが prvalue として返されます。

この変換はメモリ位置から CPU レジスタに値を読み込む動作をモデル化します。

glvalue の参照先のオブジェクトが不定値を格納している場合 (非クラスの自動変数をデフォルト初期化することによって取得した場合など)、動作は未定義です。

ただし、その不定値が CPU レジスタにキャッシュされていなかった符号なし文字型 (cv 修飾されていても構いません) である場合は除きます。 CPU レジスタにキャッシュされていなかったとは、形式的には、以下のいずれかです。

  • その記憶域期間が静的またはスレッドローカルであった。
  • それへのポインタが構築された。
  • それが参照に束縛された。

glvalue が delete によって無効化されたポインタ値を格納している場合、動作は処理系定義です (未定義ではありません)。

(C++11以上)

[編集] 配列からポインタへの変換

N 個の T の配列」または「境界が未知な T の配列」型の lvalue または rvalue は、「T へのポインタ」型の prvalue に暗黙に変換できます。 配列が prvalue の場合は、一時具体化が発生します。 (C++17以上) 結果のポインタはその配列の最初の要素を参照します (詳細については配列からポインタへの格下げを参照してください)。

一時具体化

任意の完全型 Tprvalue は、同じ型 T の xvalue に変換できます。 この変換は結果のオブジェクトとして一時オブジェクトを用いて prvalue を評価することによって prvalue から T 型の一時オブジェクトを初期化し、その一時オブジェクトを表す xvalue を生成します。 T がクラス型またはクラスの配列型の場合は、はアクセス可能かつ削除されていないデストラクタを持たなければなりません。

struct S { int m; };
int i = S().m; // C++17 以上では、メンバアクセスは glvalue を期待します。
               // S() の prvalue は xvalue に変換されます。

一時具体化は以下の状況で発生します。

  • prvalue への参照を束縛するとき。
  • クラスの prvalue に対してメンバアクセスを行うとき。
  • 配列からポインタへの変換 (上を参照) または配列の prvalue に対する添字アクセス を行うとき。
  • 波括弧初期化子リストから std::initializer_list<T> 型のオブジェクトを初期化するとき。
  • typeid が prvalue (これは未評価式の部分です) に適用されるとき。
  • sizeof が prvalue (これは未評価式の部分です) に適用されるとき。
  • 値を破棄する式として prvalue が現れるとき。
(C++17以上)

[編集] 関数からポインタへの変換

関数型 Tlvalue はその関数へのポインタprvalue に暗黙に変換されます。 非静的メンバ関数を参照する lvalue は存在しないため、これは非静的メンバ関数には適用されません。

[編集] 数値昇格

[編集] 整数昇格

小さな整数型 (char など) の prvalue は、より大きな整数型 (int など) の prvalue に変換できます。 特に、算術演算子は引数として int より小さな型を受理せず、左辺値から右辺値への変換後、整数昇格が自動的に適用されます (適用可能であれば)。 この変換は常に値を維持します。

以下の暗黙の変換は整数昇格に分類されます。

  • signed char または signed shortint に変換できます。
  • unsigned charchar8_t (C++20以上) または unsigned short は、その値の範囲全体を保持することができる場合は int に、そうでなければ unsigned int に変換できます。
  • char は、そのベースとなる型 (signed char または unsigned char (上を参照)) によって、 int または unsigned int に変換できます。
  • wchar_tchar16_t および char32_t (C++11以上) は、 intunsigned intlongunsigned longlong longunsigned long long (C++11以上) のうち、その値の範囲全体を保持することができる最初の型に変換できます。
  • ベースとなる型が固定されていないスコープなし列挙は、 intunsigned intlongunsigned longlong longunsigned long long または拡張整数型 (サイズの順で、符号付きが符号なしより優先されます) (C++11以上) のうち、その値の範囲全体を保持することができる最初の型に変換できます。 値の範囲がより大きい場合は、整数昇格は適用されません。
  • ベースとなる型が固定されているスコープなし列挙は、そのベースとなる型に変換でき、ベースとなる型が整数昇格の対象でもある場合は、その昇格したベースとなる型に変換できます。 オーバーロード解決の目的に対しては、未昇格のベースとなる型への変換がより良いとみなされます。
(C++11以上)
  • ビットフィールドは、そのビットフィールドの値の範囲全体を int で表現できる場合は int に変換でき、そうでなくそのビットフィールドの値の範囲全体を unsigned int で表現できる場合は unsigned int に変換でき、そうでなければ整数昇格は適用されません。
  • bool 型は int に変換できます。 値 false0 になり、値 true1 になります。

他のすべての変換は昇格ではないことに注意してください。 例えば、オーバーロード解決charshort (変換) よりも charint (昇格) を選択します。

[編集] 浮動小数点昇格

float 型の prvaluedouble 型の prvalue に変換できます。 値は変更されません。

[編集] 数値変換

昇格と異なり、数値変換は値を変更することがあります。 これは精度の喪失を伴うことがあります。

[編集] 整数変換

整数型またはスコープなし列挙型の prvalue は、任意の他の整数型に変換できます。 変換が整数昇格の項目に掲載されている場合、それは昇格であって、変換ではありません。

  • 結果の型が符号なしの場合、結果の値は x modulo 2n
    と等しい最も小さな符号なしの値です。 ただし x は元の値で、 n は結果の型を表現するために使用されるビット数です。
つまり、結果の型が広くなるか狭くなるかによって、符号なし整数は符号拡張[注釈 1]または切り捨てられ、符号なし整数はゼロ拡張または切り捨てられます。
  • 結果の型が符号付きの場合、元の整数が結果の型で表現可能であれば、値は変更されません。 そうでなければ、結果は処理系定義です (C++20未満) x modulo 2n
    に等しい結果の型の一意な値です (ただし x は元の値で n は結果の型を表現するために使用されるビット数です)
    (C++20以上)
    (これは符号付整数算術のオーバーフロー (未定義です) と異なることに注意してください)。
  • 元の型が bool の場合、値 false は結果の型のゼロに変換され、値 true は 1 に変換されます (結果の型が int の場合、これは整数昇格であって、整数変換ではないことに注意してください)。
  • 結果の型が bool の場合、これはブーリアン変換 (後述) です。

[編集] 浮動小数点変換

浮動小数点型の prvalue は、任意の他の浮動小数点型の prvalue に変換できます。 変換が浮動小数点昇格の項目に掲載されている場合、それは昇格であって、変換ではありません。

  • 元の値が結果の型で正確に表現できる場合、それは変更されません。
  • 元の値が結果の型で表現可能な2つの値の間の場合、結果はその2つの値のどちらかです (どちらであるかは処理系定義です。 しかし IEEE 算術がサポートされている場合、丸めのデフォルトは最も近い値です)。
  • そうでなければ、動作は未定義です。

[編集] 浮動小数点と整数の変換

  • 浮動小数点型の prvalue は、任意の整数型の prvalue に変換できます。 小数部は切り捨てられます。 つまり、小数部は破棄されます。 値が結果の型に収まらない場合、動作は未定義です (結果の型が符号なしのときでも、モジュロ算術は適用されません)。 結果の型が bool の場合、これはブーリアン変換 (後述) です。
  • 整数型または符号なし列挙型の prvalue は、任意の浮動小数点型の prvalue に変換できます。 値が正確に表現できない場合、最も近いより大きな表現可能な値と最も近いより小さな表現可能な値のどちらが選択されるかは処理系定義です (しかし IEEE 算術がサポートされている場合、丸めのデフォルトは最も近い値です)。 値が結果の型に収まらない場合、動作は未定義です。 元の型が bool の場合、値 false はゼロに変換され、値 true は 1 に変換されます。

[編集] ポインタ変換

  • ヌルポインタ定数 (NULL を参照) は、任意のポインタ型に変換でき、結果はその型のヌルポインタ値です。 そのような変換 (ヌルポインタ変換と言います) は、 cv 修飾された型への変換を単一の変換として許可します。 つまり、数値変換と修飾子変換の組み合わせとはみなされません。
  • 任意のオブジェクト型 T (cv 修飾されていても構いません) へのポインタの prvalue は、 (同じに cv 修飾された) void へのポインタの prvalue に変換できます。 結果のポインタは元のポインタ値と同じメモリ内の位置を表します。 元のポインタがヌルポインタ値の場合、結果は結果の型のヌルポインタ値です。
  • 派生クラス型 (cv 修飾されていても構いません) へのポインタの prvalue は、 (同じに cv 修飾された) その基底クラスへのポインタの prvalue に変換できます。 基底クラスがアクセス不可能または曖昧な場合、変換は ill-formed です (コンパイルできません)。 変換の結果は、その指している先のオブジェクト内の基底クラス部分オブジェクトへのポインタです。 ヌルポインタ値は結果の型のヌルポインタ値に変換されます。

[編集] メンバポインタ変換

  • ヌルポインタ定数 (NULL を参照) は、任意のメンバへのポインタ型に変換でき、結果はその型のヌルポインタ値です。 そのような変換 (ヌルメンバポインタ変換と言います) は、 cv 修飾された型への変換を単一の変換として許可します。 つまり、数値変換と修飾子変換の組み合わせとはみなされません。
  • 基底クラス B 内の何らかの型 T のメンバへのポインタの prvalue は、その派生クラス D 内の同じ型 T のメンバへのポインタの prvalue に変換できます。 B がアクセス不可能、曖昧、 D の仮想基底、または D の何らかの中間仮想基底の基底である場合、変換は ill-formed です (コンパイルできません)。 結果のポインタは D オブジェクトを用いて逆参照でき、その D オブジェクトの B 基底部分オブジェクト内のメンバにアクセスします。 ヌルポインタ値は結果の型のヌルポインタ値に変換されます。

[編集] ブーリアン変換

整数型、浮動小数点型、スコープなし列挙型、ポインタ型、メンバへのポインタ型の prvalue は、 bool 型の prvalue に変換できます。

値ゼロ (整数、浮動小数点、符号なし列挙の場合)、ヌルポインタ値、ヌルメンバポインタ値は false になります。 他のすべての値は true になります。

直接初期化の文脈では、 bool オブジェクトは std::nullptr_t 型の prvalue (nullptr を含みます) から初期化できます。 結果の値は false です。 しかし、これは暗黙の変換であるとはみなされません。

(C++11以上)

[編集] 修飾子変換

  • cv 修飾された型 T へのポインタ型の prvalue は、より多く cv 修飾された同じ型 T へのポインタの prvalue に変換できます (別の言い方をすると、 const および volatile は追加できます)。
  • クラス X 内の cv 修飾された型 T のメンバへのポインタ型の prvalue は、クラス X 内のより多く cv 修飾された型 T のメンバへのポインタの prvalue に変換できます。

「より多く」 cv 修飾されたとは、以下のことを意味します。

  • 無修飾型へのポインタは const へのポインタに変換できます。
  • 無修飾型へのポインタは volatile へのポインタに変換できます。
  • 無修飾型へのポインタは const volatile へのポインタに変換できます。
  • const 型へのポインタは const volatile へのポインタに変換できます。
  • volatile 型へのポインタは const volatile へのポインタに変換できます。

多段ポインタの場合は、以下の制限が適用されます。 cv1
n
修飾された T への cv1
n-1
修飾されたポインタへの ... への cv1
1
修飾されたポインタへの cv1
0
修飾されたポインタである多段ポインタ P1 は、以下の場合にのみ、 cv2
n
修飾された T への cv2
n-1
修飾されたポインタへの ... への cv2
1
修飾されたポインタへの cv2
0
修飾されたポインタである多段ポインタ P2 に変換できます。

  • 両方のポインタについて、段数 n が同じである。
  • P1 の (0段目以外の) 何らかの段の cv1
    k
    修飾に const が存在する場合、 P2 の同じ段の cv2
    k
    const が存在する。
  • P1 の (0段目以外の) 何らかの段の cv1
    k
    修飾に volatile が存在する場合、 P2 の同じ段の cv2
    k
    volatile が存在する。
  • 何らかの段 k において、 P2P1 より多く cv 修飾されている場合、 P2 の k 段目までの (0段目以外の) すべての段 cv2
    1
    , cv2
    2
    ... cv2
    k
    において、 const が存在しなければなりません。
  • 同じルールが多段メンバへのポインタおよびオブジェクトへのポインタとメンバへのポインタの多段混合ポインタにも適用されます。
  • 同じルールが任意の段において境界が既知または未知の配列へのポインタを含む多段ポインタにも適用されます (cv 修飾された要素の配列は同じに cv 修飾されたそれら自身であるとみなされます)。
(C++14以上)
  • 0段目は多段でない修飾子変換に対するルールによって扱われます。
char** p = 0;
const char** p1 = p; // エラー、2段目はより多く cv 修飾されていますが、1段目は const ではありません。
const char* const * p2 = p; // OK、2段目はより多く cv 修飾され、1段目は const が追加されています。
volatile char * const * p3 = p; // OK、2段目はより多く cv 修飾され、1段目は const が追加されています。
volatile const char* const* p4 = p2; // OK、2段目はより多く cv 修飾され、1段目はすでに const でした。
 
double *a[2][3];
double const * const (*ap)[3] = a; // C++14 以上では OK。

C 言語では、 const/volatile を追加できるのは最初の段のみであることに注意してください。

char** p = 0;
char * const* p1 = p; // C でも C++ でも OK。
const char* const * p2 = p; // C ではエラー、 C++ では OK。

関数ポインタ変換

  • 例外を投げない関数へのポインタ型の prvalue は例外を投げる可能性のある関数へのポインタの prvalue に変換できます。
  • 例外を投げないメンバ関数へのポインタ型の prvalue は、例外を投げる可能性のあるメンバ関数へのポインタの prvalue に変換できます。
void (*p)();
void (**pp)() noexcept = &p; // エラー、 noexcept 関数へのポインタには変換できません。
 
struct S
{
    typedef void (*p)();
    operator p();
};
void (*q)() noexcept = S(); // エラー、 noexcept 関数へのポインタには変換できません。
(C++17以上)

[編集] 安全な bool の問題

C++11 で explicit 変換関数が導入されるまで、ブーリアンの文脈 (例えば if(obj) { ... }) で使用可能であるべきクラスの設計は、ある問題を提示しました。 T::operator bool() const; のようなユーザ定義変換関数を与えると、暗黙の変換シーケンスはその関数呼び出しの後にもうひとつ追加の標準変換を許容しました。 つまり、結果の boolint に変換することができ、 obj << 1;int i = obj; のようなコードを許容しました。

これに対する初期の解決方法のひとつは std::basic_ios で見られます。 std::basic_iosoperator!operator void*(C++11未満) を定義しています。 これにより if(std::cin) {...} のようなコードは void*bool に変換可能なためコンパイルできますが、 int n = std::cout; のようなコードは void*int に変換可能でないためコンパイルできません。 これはまだ delete std::cout; のようなナンセンスなコードを許容するので、 多くの C++11 前のサードパーティライブラリは、安全な bool のイディオムとして知られる、よりエレガントな解決方法を用いて設計されました。

explicit bool 変換は安全な bool の問題を解決するためにも使用できます。

explicit operator bool() const { ... }
(C++11以上)

[編集] 注釈

  1. これは算術が2の補数の場合にのみ適用されます。 2の補数算術は固定幅の整数型に対してのみ要求されます。 しかし、現在のところ、 C++ コンパイラが存在するすべてのプラットフォームは2の補数算術を使用していることに注意してください。

[編集] 欠陥報告

以下の動作変更欠陥報告は以前に発行された C++ 標準に遡って適用されました。

DR 適用先 発行時の動作 正しい動作
CWG 330 C++14 conversion from double * const (*p)[3] to double const * const (*p)[3] invalid conversion valid
CWG 616 C++11 lvalue to rvalue conversion of any uninitialized object was always UB indeterminate unsigned char is allowed
CWG 1423 C++11 std::nullptr_t is convertible to bool in both direct- and copy-initialization direct-initialization only
CWG 1781 C++11 std::nullptr_t to bool is considered a implicit conversion even though it is only valid for direct-initialization no longer considered an implicit conversion

[編集] 関連項目

暗黙の変換C言語リファレンス