Namespaces
Variants

Implicit conversions

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Implicit conversions
static_cast
const_cast
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

暗黙の変換は、ある型 T1 の式がその型を受け付けないが、他の型 T2 を受け付ける文脈で使用される際に実行されます。具体的には:

  • 式が、引数として T2 型のパラメータで宣言された関数を呼び出す際の引数として使用される場合;
  • 式が、 T2 型を期待する演算子のオペランドとして使用される場合;
  • T2 型の新しいオブジェクトを初期化する場合( T2 を返す関数の return 文を含む);
  • 式が switch 文で使用される場合( T2 は整数型);
  • 式が if 文またはループで使用される場合( T2 bool 型)。

プログラムは、 T1 から T2 への明確な1つの 暗黙の変換シーケンス が存在する場合にのみ、正しい形式(コンパイル可能)となります。

呼び出される関数または演算子に複数のオーバーロードが存在する場合、 T1 から利用可能な各 T2 への暗黙変換シーケンスが構築された後、 オーバーロード解決 のルールに基づいて、どのオーバーロードをコンパイルするかが決定されます。

注記: 算術式において、二項演算子の被演算子に対する暗黙の変換の変換先の型は、別個の規則によって決定されます: usual arithmetic conversions .

目次

変換の順序

暗黙の変換シーケンスは以下の順序で構成されます:

1) ゼロまたは1つの standard conversion sequence ;
2) ゼロまたは1つの user-defined conversion ;
3) 0個または1個の standard conversion sequence (ユーザー定義変換が使用される場合のみ)。

コンストラクタまたはユーザー定義変換関数の引数を考慮する際、許可される標準変換シーケンスは1つだけです(そうでないと、ユーザー定義変換が効果的に連鎖できてしまいます)。ある非クラス型から別の非クラス型への変換では、標準変換シーケンスのみが許可されます。

標準変換シーケンスは、以下の順序で構成されます:

1) 以下のセットからのゼロ回または一回の変換:
  • lvalue-to-rvalue conversion ,
  • array-to-pointer conversion , および
  • function-to-pointer conversion ;
2) ゼロまたは1回の numeric promotion または numeric conversion ;
3) ゼロまたは1つの function pointer conversion ;
(C++17以降)
4) ゼロまたは1つの qualification conversion

ユーザー定義変換は、ゼロまたは1つの非明示的単一引数 converting constructor または非明示的 conversion function の呼び出しで構成されます。

e T2 へ暗黙変換可能 であるとは、 T2 e から コピー初期化 可能である場合、すなわち宣言 T2 t = e ; が(コンパイル可能な)適切な形式である場合を指す。ここで t は仮想的な一時変数を表す。これは 直接初期化 ( T2 t ( e ) ) とは異なり、後者では明示的コンストラクタと変換関数も追加で考慮される点に注意。

コンテキスト変換

以下の文脈では、型 bool が期待され、宣言 bool t ( e ) ; が適正であれば(つまり、 explicit T :: operator bool ( ) const ; のような明示的変換関数も考慮される)、暗黙変換が実行される。このような式 e 文脈的に bool に変換される と言われる。

  • if while for の制御式
  • 組み込み論理演算子 ! && || のオペランド
  • 条件演算子 ?: の第1オペランド
  • static_assert 宣言内の述語
  • noexcept 指定子内の式
(C++20以降)
(C++11以降)

以下の文脈では、文脈固有の型 T が期待され、クラス型 E の式 e は、次の場合にのみ許可されます

(C++14まで)
  • 許容可能な型の中に厳密に1つの型 T が存在し、 E が戻り値型が(修飾された可能性のある) T または(修飾された可能性のある) T への参照である非明示的変換関数を持ち、かつ
  • e T に暗黙的に変換可能である。
(C++14以降)

そのような式 e は、指定された型 T 文脈的に暗黙変換される と言います。 明示的変換関数は考慮されないことに注意してください。ただし、 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までエラー(複数の変換関数が存在)
                      // C++14以降OK(両関数とも同じ型intに変換)
    switch (i + 0) {} // 常に問題なし(暗黙変換)
}

値変換

値変換は式の value category を変更する変換です。これらは、式が異なるvalue categoryの式を期待する演算子のオペランドとして現れるときに常に発生します:

  • グリーバリューがオペランドとして現れ、そのオペランドにプラバリューを必要とする演算子の対象となる場合、 lvalue-to-rvalue array-to-pointer 、または function-to-pointer 標準変換が適用され、式がプラバリューに変換されます。
  • 特に指定がない限り、prvalueがそのオペランドに対してglvalueを期待する演算子のオペランドとして現れる場合、 temporary materialization conversion が適用され、その式がxvalueに変換されます。
(C++17以降)

Lvalue-to-rvalue変換

任意の非関数、非配列型 lvalue (C++11以前) 任意の非関数、非配列型 glvalue (C++11以降) T は暗黙的に rvalue (C++11以前) prvalue (C++11以降) に変換できます:

  • T がクラス型でない場合、 rvalue (until C++11) prvalue (since C++11) の型は T のCV修飾なしのバージョンである。
  • それ以外の場合、 rvalue (until C++11) prvalue (since C++11) の型は T である。

不完全型からの左辺値から右辺値への変換がプログラムによって要求される場合、そのプログラムは不適格です。

指定されたオブジェクト( lvalue (until C++11) glvalue (since C++11) が参照するオブジェクト)を obj として:

  • lvalue-to-rvalue変換が sizeof 演算子のオペランド内で発生する場合、 obj に含まれる値はアクセスされません。この演算子はオペランドを 評価しない ためです。
  • 変換の結果は obj に含まれる値です。 T obj の型の一方が符号付き整数型で、他方が対応する符号なし整数型である場合、結果は obj と同じ値表現を持つ T 型の値です。
(C++11まで)
  • lvalue-to-rvalue変換が式 E に適用される場合、 obj に含まれる値は以下の場合にアクセスされません:
  • 変換の結果は以下のように決定されます:
  • T が(cv修飾されている可能性のある) std::nullptr_t の場合、結果は nullポインタ定数 です。 obj は変換によってアクセスされないため、 T がvolatile修飾されていても副作用はなく、glvalueはunionの非アクティブメンバーを参照できます。
  • それ以外の場合、 T がクラス型の場合:
(C++17まで)
(C++17以降)
  • それ以外の場合、 obj が無効なポインタ値を含む場合、動作は実装定義です。
  • それ以外の場合、 obj 値表現 のビットが obj の型に対して有効でない場合、動作は未定義です。
  • それ以外の場合、 obj が読み込まれ、 (C++20以降) 結果は obj に含まれる値です。 T obj の型の一方が符号付き整数型で、他方が対応する符号なし整数型である場合、結果は obj と同じ値表現を持つ T 型の値です。
(C++11以降)

この変換は、メモリ位置から値を読み出してCPUレジスタに格納する動作をモデル化します。

配列からポインタへの変換

型「 N 個の T の配列」または「境界不明の T の配列」の lvalue または rvalue は、「 T へのポインタ」型の prvalue に暗黙的に変換できます。 配列がprvalueの場合、 一時オブジェクト化 が発生します。 (C++17以降) 結果のポインタは配列の最初の要素を指します(詳細は 配列からポインタへの減衰 を参照)。

関数からポインタへの変換

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

一時的な実体化 (Temporary materialization)

任意の完全型 T prvalue は、同じ型 T のxvalueに変換できます。この変換は、prvalueをその結果オブジェクトとして一時オブジェクトで評価することにより、prvalueから型Tの 一時オブジェクト を初期化し、その一時オブジェクトを示すxvalueを生成します。

T がクラス型またはクラス型の配列である場合、アクセス可能で削除されていないデストラクタを持たなければなりません。

struct S { int m; };
int i = S().m; // member access expects glvalue as of C++17;
               // S() prvalue is converted to xvalue

一時的な実体化は以下の状況で発生します:

同じ型のprvalueからオブジェクトを初期化する場合( 直接初期化 または コピー初期化 )には、一時的な実体化は発生 しません :そのようなオブジェクトは初期化子から直接初期化されます。これにより「保証されたコピー省略」が確保されます。

(C++17以降)

整数プロモーション

prvalues 小規模な整数型(例: char )およびスコープなし列挙型のprvaluesは、より大きな整数型(例: int )のprvaluesに変換されることがあります。特に、 算術演算子 int より小さい型を引数として受け付けず、該当する場合、左辺値から右辺値への変換後に整数プロモーションが自動的に適用されます。この変換は常に値を保持します。

以下のセクションで説明する暗黙の変換は integral promotions に分類されます。

与えられたソース型に対して、整数昇格の宛先型は一意であることに注意してください。そして他のすべての変換は昇格ではありません。例えば、 オーバーロード解決 char -> int (昇格) を char -> short (変換) よりも優先して選択します。

整数型からのプロモーション

bool 型のprvalueは、 int 型のprvalueに変換することができ、 false 0 に、 true 1 になります。

ブール型 bool を除く、整数型 T の純粋右辺値 val について:

1) もし val ビットフィールド に適用された左辺値から右辺値への変換の結果である場合、
  • val は、 int がビットフィールドのすべての値を表現できる場合、 int 型のprvalueに変換可能である;
  • そうでなければ、 val は、 unsigned int がビットフィールドのすべての値を表現できる場合、 unsigned int に変換可能である;
  • そうでなければ、 val は、項目(3)で指定された規則に従って変換可能である。
2) それ以外の場合( val がビットフィールドから変換されない場合)、
  • もし T char8_t , (C++20以降) char16_t , char32_t または (C++11以降) wchar_t であれば、 val は項目(3)で指定された規則に従って変換可能;
  • それ以外の場合、 T 整数変換ランク int のランクより低い場合:
  • val int 型のprvalueに変換可能(もし int T のすべての値を表現できる場合);
  • それ以外の場合、 val unsigned int 型のprvalueに変換可能。
3) 項目(1)(変換されたビットフィールドが unsigned int に適合しない場合)または項目(2)( T が指定された文字型のいずれかである場合)で指定される場合、 val は、その基盤型のすべての値を表現できる最初の以下の型のprvalueに変換できます:
  • int
  • unsigned int
  • long
  • unsigned long
  • long long
  • unsigned long long
  • T の基盤型
(C++11以降)

列挙型からの昇格

基となる型が固定されていないスコープなし enum 型のprvalueは、その値の全範囲を保持できる以下のリストの最初の型のprvalueに変換できます:

  • int
  • unsigned int
  • long
  • unsigned long
  • long long
  • unsigned long long
  • 拡張整数型 であり、以下の条件を満たすもの
  • 整数変換ランク long long よりも高い
  • 整数変換ランクがすべての拡張整数型の中で最も低い
  • すべての拡張整数型の中で最も低い整数変換ランクを持つ型が2つ存在する場合、符号付きである
(C++11以降)


基となる型が固定されているスコープなし列挙型の純粋右辺値は、その基となる型に変換できます。さらに、基となる型が整数昇格の対象となる場合、昇格された基となる型に変換できます。オーバーロード解決の目的では、昇格されていない基となる型への変換が優先されます。

(C++11以降)

浮動小数点プロモーション

float 型の prvalue double 型のprvalueに変換できます。値は変化しません。

この変換は floating-point promotion と呼ばれます。

数値変換

プロモーションとは異なり、数値変換は値を変更する可能性があり、精度の損失が生じる恐れがあります。

整数変換

整数型またはスコープなし列挙型の prvalue は、他の任意の整数型に変換できます。変換が整数昇格に該当する場合、それは変換ではなく昇格となります。

  • 変換先の型が符号なしの場合、結果の値は変換元の値を 2 n
    とする最小の符号なし値となります。ここで n は変換先の型を表現するために使用されるビット数です。
  • つまり、宛先の型がより広いか狭いかに応じて、符号付き整数は符号拡張 [1] または切り詰められ、符号なし整数はそれぞれゼロ拡張または切り詰められます。
  • 変換先の型が符号付きの場合、元の整数が変換先の型で表現可能であれば値は変化しない。それ以外の場合、結果は 実装定義 (C++20まで) 変換先の型のビット数 n を用いて、元の値を 2 n
    で割った剰余に等しい変換先の型の一意な値
    (C++20以降)
    となる (これは 符号付き整数算術オーバーフロー が未定義動作であることとは異なる)。
  • 元の型が bool の場合、値 false はゼロに変換され、値 true は変換先の型の値1に変換される (変換先の型が int の場合、これは整数変換ではなく整数プロモーションであることに注意)。
  • 変換先の型が bool の場合、これは ブーリアン変換 となる(後述)。
  1. これは exact-width integer types に対してのみ要求される2の補数表現が使用されている場合にのみ適用されます。ただし、現在のところC++コンパイラが利用可能なすべてのプラットフォームは2の補数演算を使用していることに注意してください。

浮動小数点変換

浮動小数点型の prvalue は、他の任意の浮動小数点型のprvalueに変換できます。

(C++23まで)

浮動小数点型の prvalue は、同等またはより高い 浮動小数点変換ランク を持つ他の任意の浮動小数点型のprvalueに変換できます。

標準浮動小数点型の prvalue は、他の任意の標準浮動小数点型のprvalueに変換できます。

static_cast を使用して、浮動小数点型のprvalueを明示的に他の任意の浮動小数点型に変換できます。

(C++23以降)

変換が浮動小数点の昇格に該当する場合、それは変換ではなく昇格です。

  • 変換元の値が変換先の型で正確に表現できる場合、値は変更されません。
  • 変換元の値が変換先の型で表現可能な2つの値の中間にある場合、結果はそれら2つの値のいずれかになります(どちらになるかは実装定義ですが、IEEE演算がサポートされている場合、丸めモードはデフォルトで 最近接偶数丸め となります)。
  • それ以外の場合、動作は未定義です。

浮動小数点-整数変換

浮動小数点型の prvalue は、任意の整数型のprvalueに変換できます。小数部分は切り捨てられます。つまり、小数部分は破棄されます。

  • 切り詰められた値が変換先の型に適合できない場合、動作は未定義です(変換先の型が符号なしであっても、剰余演算は適用されません)。
  • 変換先の型が bool の場合、これはブーリアン変換です( 後述 を参照)。

整数型またはスコープなし列挙型のprvalueは、任意の浮動小数点型のprvalueに変換できます。可能な場合は結果は正確になります。

  • 値が変換先の型に収まるが正確に表現できない場合、最も近い上位表現可能値と最も近い下位表現可能値のどちらが選択されるかは実装定義である。ただしIEEE演算がサポートされている場合、丸めモードはデフォルトで 最近接偶数丸め となる。
  • 値が変換先の型に収まらない場合、動作は未定義である。
  • ソース型が bool の場合、値 false はゼロに変換され、値 true は1に変換される。

ポインタ変換

null pointer constant は任意のポインタ型に変換でき、その結果はその型のnullポインタ値となります。このような変換( null pointer conversion として知られる)は、修飾された型への単一の変換として許可されており、つまり数値変換と修飾変換の組み合わせとは見なされません。

任意の(オプションでCV修飾された)オブジェクト型 T への prvalue ポインタは、(同一にCV修飾された) void へのprvalueポインタに変換できます。結果のポインタは元のポインタ値と同じメモリ位置を表します。

  • 元のポインタがヌルポインタ値の場合、結果は変換先の型のヌルポインタ値となります。

型「(cv修飾された可能性のある) Derived へのポインタ」のprvalueは、 型「(cv修飾された可能性のある) Base へのポインタ」のprvalueに変換できます。 ここで Base Derived 基底クラス であり、 Derived 完全な クラス型です。 Base がアクセス不能または曖昧な場合、プログラムは不適格となります。

  • ptr が null ポインタ値の場合、結果も null ポインタ値となる。
  • それ以外の場合、 Base Derived 仮想基本クラス であり、かつ ptr Derived 類似 した型のオブジェクトを指しておらず、そのオブジェクトが 生存期間 内または構築・破棄期間内にない場合、動作は未定義となる。
  • それ以外の場合、結果は派生クラスオブジェクトの基本クラス部分オブジェクトへのポインタとなる。

メンバへのポインタ変換

null pointer constant は任意のメンバポインタ型に変換でき、結果はその型の null メンバポインタ値となります。このような変換( null member pointer conversion として知られる)は、修飾された型への単一の変換として許可されており、つまり、数値変換と修飾変換の組み合わせとは見なされません。

Base の型(修飾の有無は問わず) T へのメンバポインタ」型の prvalue は、「 Derived の型(同一の修飾を持つ) T へのメンバポインタ」型のprvalueに変換できます。ここで Base Derived の基底クラスであり、 Derived は完全なクラス型です。 Base Derived の非公開、曖昧、または仮想基底クラスである場合、あるいは Derived の中間仮想基底クラスの基底クラスである場合、プログラムは不適格となります。

  • Derived が元のメンバーを含まず、元のメンバーを含むクラスの基底クラスでもない場合、動作は未定義です。
  • それ以外の場合、結果のポインタは Derived オブジェクトで逆参照でき、その Derived オブジェクトの Base 基底サブオブジェクト内のメンバーにアクセスします。

ブール変換

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

値ゼロ(整数型、浮動小数点型、およびスコープなし列挙型の場合)およびヌルポインタとヌルポインタ-to-member値は false になります。その他のすべての値は true になります。

直接初期化 の文脈において、 bool オブジェクトは std::nullptr_t 型のprvalue( nullptr を含む)から初期化可能である。結果の値は false となる。ただし、これは暗黙変換とは見なされない。

(C++11以降)

修飾子変換

一般的に言うと:

  • prvalue 型のポインタから cv修飾 された型 T へのprvalueは、よりcv修飾された同じ型 T へのポインタのprvalueに変換できます(つまり、const性とvolatile性を追加できます)。
  • cv修飾された型 T のクラス X におけるメンバーポインタのprvalueは、 よりcv修飾された T のクラス X におけるメンバーポインタのprvalueに変換できます。

「修飾変換」の正式な定義は 以下 に示されています。

類似の型

非公式には、2つの型は、トップレベルのcv修飾を無視した場合に similar であると言います:

  • それらが同じ型である場合、または
  • 両方がポインタであり、指し示す型が類似している場合、または
  • 両方が同じクラスのメンバへのポインタであり、指し示すメンバの型が類似している場合、または
  • 両方が配列であり、配列要素の型が類似している場合。

例:

  • const int * const * int ** は類似している;
  • int ( * ) ( int * ) int ( * ) ( const int * ) は類似していない;
  • const int ( * ) ( int * ) int ( * ) ( int * ) は類似していない;
  • int ( * ) ( int * const ) int ( * ) ( int * ) は類似している(同じ型である);
  • std:: pair < int , int > std:: pair < const int , int > は類似していない。

形式的には、型類似性は資格分解(qualification-decomposition)の観点から定義されます。

T 修飾分解 は、非負の n に対して T が「 cv_0 P_0 cv_1 P_1 ... cv_n−1 P_n−1 cv_n U 」となるような、コンポーネント cv_i P_i の列である。ここで

  • each cv_i const volatile のセットであり、かつ
  • each P_i
  • 「〜へのポインタ」、
  • 「クラス C_i の型〜のメンバへのポインタ」、
  • N_i 個の要素からなる配列」、または
  • 「要素数不明の配列」。

P_i が配列を指定する場合、要素型のcv修飾子 cv_i+1 は配列のcv修飾子 cv_i としても扱われます。

// T は「const int へのポインタへのポインタ」であり、3つの修飾分解を持つ:
// n = 0 -> cv_0 は空、U は「const int へのポインタへのポインタ」
// n = 1 -> cv_0 は空、P_0 は「ポインタ」、
//          cv_1 は空、U は「const int へのポインタ」
// n = 2 -> cv_0 は空、P_0 は「ポインタ」、
//          cv_1 は空、P_1 は「ポインタ」、
//          cv_2 は「const」、U は「int」
using T = const int**;
// 以下のいずれかの型を U に代入すると、いずれかの分解が得られる:
// U = U0 -> n = 0 の分解: U0
// U = U1 -> n = 1 の分解: ポインタ [U1]
// U = U2 -> n = 2 の分解: ポインタ [ポインタ [const U2]]
using U2 = int;
using U1 = const U2*;
using U0 = U1*;

2つの型 T1 T2 は、それぞれに対して修飾分解が存在し、2つの修飾分解に対して以下のすべての条件が満たされる場合、 類似している とされます:

  • それらは同じ n を持つ。
  • U で示される型は同じである。
  • 対応する P_i コンポーネントはすべての i について同じである または、一方が「 N_i の配列」で他方が「境界未知の配列」である (C++20以降)
// n = 2 での修飾分解:
// [const intへのvolatileポインタ]へのポインタ
using T1 = const int* volatile *;
// n = 2 での修飾分解:
// [intへのポインタ]へのconstポインタ
using T2 = int** const;
// 上記2つの修飾分解について
// cv_0、cv_1、cv_2はすべて異なるが、
// 同じn、U、P_0、P_1を持つため、
// 型T1とT2は類似している。

cv修飾子の組み合わせ

以下の説明において、型 Tn の最長修飾分解は Dn と表記され、その構成要素は cvn_i および Pn_i と表記されます。

T1 のprvalue式は、以下の条件がすべて満たされる場合に型 T2 に変換可能です:

  • T1 T2 が類似している。
  • すべての非ゼロ i について、 cv1_i const が含まれる場合、 cv2_i にも const が含まれ、同様に volatile についても同様である。
  • すべての非ゼロ i について、 cv1_i cv2_i が異なる場合、 [ 1 , i ) 内のすべての k について cv2_k const が含まれる。

二つの型 T1 T2 修飾子結合型 は、以下の条件を満たす T1 に類似した型 T3 です:

  • cv3_0 が空である。
  • すべての非ゼロ i について、 cv3_i cv1_i cv2_i の和集合である。
  • cv3_i cv1_i または c2_i と異なる場合、 [ 1 , i ) 内のすべての k について cv3_k const が追加される。
(C++20以前)

二つの型 T1 T2 修飾子結合型 は、以下の条件をすべて満たす T1 に類似した型 T3 であり、 D3 は以下の条件を満たします:

  • cv3_0 が空である。
  • すべての非ゼロ i について、 cv3_i cv1_i cv2_i の和集合である。
  • P1_i または P2_i が「境界不明の配列」の場合、 P3_i は「境界不明の配列」であり、それ以外の場合は P1_i である。
  • cv3_i cv1_i または cv2_i と異なる場合、または P3_i P1_i または P2_i と異なる場合、 [ 1 , i ) 内のすべての k について cv3_k const が追加される。

T1 のprvalueは、 T1 T2 の修飾子結合型がcv-unqualifiedな T2 である場合に型 T2 に変換可能です。

(C++20以降)
// T1の最長修飾分解 (n = 2):
// [char]へのポインタへのポインタ
using T1 = char**;
// T2の最長修飾分解 (n = 2):
// [const char]へのポインタへのポインタ
using T2 = const char**;
// D3のcv3_iとT_iコンポーネントの決定 (n = 2):
// cv3_1 = 空 (空のcv1_1と空のcv2_1の和集合)
// cv3_2 = "const" (空のcv1_2と"const" cv2_2の和集合)
// P3_0 = "pointer to" (未知の境界を持つ配列なし、P1_0を使用)
// P3_1 = "pointer to" (未知の境界を持つ配列なし、P1_1を使用)
// cv_2以外のすべてのコンポーネントは同じ、cv3_2はcv1_2と異なるため、
// [1, 2)の各kに対してcv3_kに"const"を追加: cv3_1が"const"になる
// T3は「const charへのconstポインタへのポインタ」、すなわちconst char* const *
using T3 = /* T1とT2の修飾結合型 */;
int main()
{
    const char c = 'c';
    char* pc;
    T1 ppc = &pc;
    T2 pcc = ppc; // エラー: T3はcv修飾されていないT2と同じではない、
                  //        暗黙変換なし
    *pcc = &c;
    *pc = 'C';    // もし上記の誤った代入が許可された場合、
                  // constオブジェクト「c」が変更される可能性がある
}

Cプログラミング言語では、 const / volatile は第一レベルにのみ追加できることに注意してください:

char** p = 0;
char * const* p1 = p;       // CとC++の両方で有効
const char* const * p2 = p; // Cではエラー、C++では有効

関数ポインタ変換

  • 非スロー関数へのポインタ型の prvalue は、潜在的にスローする可能性のある関数へのポインタのprvalueに変換可能
  • 非スローメンバ関数へのポインタ型のprvalueは、潜在的にスローする可能性のあるメンバ関数へのポインタのprvalueに変換可能
void (*p)();
void (**pp)() noexcept = &p; // error: cannot convert to pointer to noexcept function
struct S
{
    typedef void (*p)();
    operator p();
};
void (*q)() noexcept = S(); // error: cannot convert to pointer to noexcept function
(C++17以降)

セーフブール問題

C++11以前、ブーリアンコンテキストで使用可能なクラス(例えば if ( obj ) { ... } )を設計する際には問題があった:ユーザー定義変換関数(例: T :: operator bool ( ) const ; )が与えられた場合、暗黙の変換シーケンスではその関数呼び出し後に追加の標準変換シーケンスが許可される。これは結果の bool int に変換される可能性があることを意味し、 obj << 1 ; int i = obj ; のようなコードが許可されてしまう。

この問題に対する初期の解決策の一つは std::basic_ios に見ることができます。これは当初 operator void * を定義していたため、 if ( std:: cin ) { ... } のようなコードは、 void * bool に変換可能であるためコンパイルされます。しかし int n = std:: cout ; は、 void * int に変換不可能であるためコンパイルされません。この方法でもなお、 delete std:: cout ; のような無意味なコードがコンパイルされてしまう問題が残っていました。

C++11以前の多くのサードパーティライブラリは、より精巧な解決策である Safe Boolイディオム を採用していました。 std::basic_ios LWG issue 468 を通じてこのイディオムを許可しており、 operator void * は置き換えられました( 注記 を参照)。

C++11以降、 explicit bool変換 を使用してセーフブール問題を解決することも可能です。

不具合報告

以下の動作変更の欠陥報告書は、以前に公開されたC++規格に対して遡及的に適用されました。

DR 適用対象 公開時の動作 正しい動作
CWG 170 C++98 メンバポインタ変換の動作が不明確であった
派生クラスが元のメンバを持たない場合
明確化された
CWG 172 C++98 列挙型の昇格がその基盤となる型に基づいて行われていた 代わりにその値の範囲に基づいて行われるように変更
CWG 330
( N4261 )
C++98 double * const ( * p ) [ 3 ] からの変換
double const * const ( * p ) [ 3 ] へが無効であった
有効に変更
CWG 519 C++98 ヌルポインタ値は他のポインタ型に変換する際に
保持されることが保証されていなかった
常に保持される
CWG 616 C++98 初期化されていないオブジェクトおよび無効な値の
ポインタオブジェクトに対する左辺値から右辺値への
変換の動作は常に未定義であった
不定値の unsigned char
は許可される; 無効なポインタの使用は
実装定義となる
CWG 685 C++98 列挙型の基盤型は、固定されている場合でも
整数プロモーションで優先されなかった
優先される
CWG 707 C++98 整数から浮動小数点への変換
はすべての場合で定義された動作を持っていた
変換される値が
変換先の範囲外である場合
動作は未定義となる
CWG 1423 C++11 std::nullptr_t は直接初期化とコピー初期化の両方で bool に変換可能であった 直接初期化のみ
CWG 1773 C++11 潜在的に評価される式に現れる名前式において、
オブジェクトがodr-usedされない場合でも、
左辺値から右辺値への変換中に評価される可能性がある
評価されない
CWG 1781 C++11 std::nullptr_t から bool への変換は、直接初期化でのみ有効であるにもかかわらず、
暗黙変換と見なされていた
暗黙変換とは見なされ
なくなった
CWG 1787 C++98 レジスタにキャッシュされた不定値の
unsigned char からの読み取り動作は未定義だった
明確に定義された動作に変更
CWG 1981 C++11 文脈的変換が明示的変換関数と見なされる 考慮されない
CWG 2140 C++11 lvalueからrvalueへの変換が
std::nullptr_t のlvalueからメモリからこれらのlvalueをフェッチするかどうかが不明確であった
フェッチされない
CWG 2310 C++98 派生クラスから基底クラスへのポインタ変換および
基底クラスから派生クラスへのポインタ-to-member変換において、
派生クラス型が不完全でも可能であった
完全型でなければならない
CWG 2484 C++20 char8_t char16_t は異なる整数昇格戦略を持っていたが、
両方に対応することが可能である
char8_t char16_t と同じ方法で
昇格されるべきである
CWG 2485 C++98 ビットフィールドを含む整数昇格の仕様が十分に明確ではなかった 仕様を改善
CWG 2813 C++23 クラスprvalueの明示的
オブジェクトメンバー関数が呼び出される際に一時オブジェクト実体化が発生する
この場合には発生しない
CWG 2861 C++98 型にアクセスできないオブジェクトへのポインタを
基底クラスのサブオブジェクトへのポインタに変換できた
この場合の動作は
未定義である
CWG 2879 C++17 一時的な実体化変換が、左辺値を期待する演算子のオペランドとして
右辺値に適用された
一部の場合に適用されない
CWG 2899 C++98 左辺値から右辺値への変換が無効な値表現を持つオブジェクトを
指定する左辺値に適用される可能性があった
この場合の動作は
未定義である
CWG 2901 C++98 unsigned int 左辺値から左辺値から右辺値への変換結果が int オブジェクトを参照し、値が - 1 の場合、不明確であった 明確化された

関連項目

C documentation for Implicit conversions
日本語訳:
Cドキュメント 暗黙の型変換
HTMLタグ、属性、C++専門用語は翻訳せず、元のフォーマットを保持しました。