名前空間
変種
操作

暗黙の型変換

From cppreference.com
< cpp‎ | language
 
 
C++言語
全般
フロー制御
条件実行文
if
繰り返し文 (ループ)
for
範囲for (C++11)
ジャンプ文
関数
関数宣言
ラムダ式
inline指定子
動的例外仕様 (C++17まで*)
noexcept指定子 (C++11)
例外
名前空間
指定子
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
記憶域期間指定子
初期化
代替表現
リテラル
ブーリアン - 整数 - 浮動小数点数
文字 - 文字列 - nullptr (C++11)
ユーザー定義 (C++11)
ユーティリティ
属性 (C++11)
typedef宣言
型エイリアス宣言 (C++11)
キャスト
暗黙の型変換
static_cast
const_cast
メモリ確保
クラス
クラス固有の関数プロパティ
explicit (C++11)
static

特殊メンバ関数
テンプレート
その他
 
 

暗黙の型変換は、ある型T1の式が、その型は受け入れないが別の型T2は受け入れるコンテキストで使用される場合に実行されます。特に、

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

プログラムは、T1からT2への一意の暗黙の変換シーケンスが存在する場合にのみ、正しく記述されている(コンパイルされる)とみなされます。

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

注:算術式では、二項演算子のオペランドに対する暗黙の型変換の宛先型は、別途の規則セットによって決定されます:通常の算術変換

目次

[編集] 変換の順序

暗黙の変換シーケンスは、次の順序で構成されます。

1) 0回または1回の標準変換シーケンス
2) 0回または1回のユーザー定義変換
3) 0回または1回の標準変換シーケンス(ユーザー定義変換が使用されている場合のみ)。

コンストラクタまたはユーザー定義変換関数への引数を考慮する場合、標準変換シーケンスは1つのみ許可されます(そうでなければ、ユーザー定義変換が実質的に連鎖される可能性があります)。非クラス型から別の非クラス型への変換では、標準変換シーケンスのみが許可されます。

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

1) 次のセットからの0回または1回の変換。
  • lvalueからrvalueへの変換,
  • 配列からポインタへの変換、および
  • 関数からポインタへの変換;
2) 0回または1回の数値昇格または数値変換
3) 0回または1回の関数ポインタ変換
(C++17以降)
4) 0回または1回のcv修飾子変換

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

eは、T2暗黙に変換可能であるといいます。これは、T2eからコピー初期化できる場合、つまり、一時的なtを導入した場合の宣言T2 t = e;が正しく記述されている(コンパイル可能である)場合にのみ当てはまります。これは、一時的なtを導入した場合の宣言T2 t(e);である直接初期化とは異なることに注意してください。直接初期化では、明示的なコンストラクタと変換関数も考慮されます。

[編集] コンテキスト依存の変換

以下のコンテキストでは、型boolが期待され、宣言bool t(e);が正しく記述されている(つまり、explicit T::operator bool() const;のような明示的な変換関数が考慮される)場合に暗黙の変換が実行されます。このような式eコンテキストに応じてboolに変換されるといいます。

  • ifwhileforの制御式。
  • 組み込み論理演算子!&&||のオペランド。
  • 条件演算子?:の最初のオペランド。
  • static_assert宣言の述語。
  • noexcept指定子の式。
  • explicit指定子の式。
(C++20以降)
(C++11以降)

以下のコンテキストでは、コンテキスト依存の型Tが期待され、クラス型Eの式eは、

(C++14まで)
  • 許容される型の中に、Eが非explicit変換関数を持ち、その戻り値の型が(cv修飾されている可能性のある)Tまたは(cv修飾されている可能性のある)Tへの参照であるようなTがちょうど1つ存在し、
  • eTに暗黙に変換可能である場合。
(C++14以降)

このような式eは、指定された型Tコンテキストに応じて暗黙に変換されるといいます。明示的な変換関数は、boolへのコンテキスト依存の変換で考慮される場合でも、考慮されないことに注意してください。(since 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) {}     // error until C++14 (more than one conversion function)
                      // OK since C++14 (both functions convert to the same type int)
    switch (i + 0) {} // always okay (implicit conversion)
}

[編集] 値の変換

値の変換とは、式の値カテゴリを変更する変換です。これは、式が異なる値カテゴリを期待する演算子のオペランドとして現れるたびに発生します。

  • glvalueが、そのオペランドにprvalueを必要とする演算子のオペランドとして現れる場合、lvalueからrvalueへの変換配列からポインタへの変換、または関数からポインタへの変換の標準変換が、式をprvalueに変換するために適用されます。
  • 特に指定がない限り、prvalueが、それを期待するglvalueのオペランドとして演算子のオペランドとして現れる場合、一時オブジェクトの生成変換が、式をxvalueに変換するために適用されます。
(C++17以降)

[編集] lvalueからrvalueへの変換

関数型でも配列型でもない任意の型Tlvalue(until C++11)任意の型Tglvalue(since C++11)は、rvalue(until C++11)prvalue(since C++11)に暗黙に変換できます。

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

プログラムが不完全型からのlvalue-to-rvalue変換を必要とする場合、そのプログラムは不正な形式となります。

lvalue(until C++11)glvalue(since C++11)が参照するオブジェクトをobjとした場合。

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

この変換は、メモリ位置からCPUレジスタに値を読み取る行為をモデル化します。

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

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

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

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

一時オブジェクトの生成

任意の完全型Tprvalueは、同じ型Tのxvalueに変換できます。この変換は、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と同じ型のオブジェクトを(直接初期化またはコピー初期化によって)prvalueから初期化する際には行われないことに注意してください。そのようなオブジェクトは初期化子から直接初期化されます。これにより「保証されたコピー削除」が実現されます。

(C++17以降)

[編集] 整数昇格

小さな整数型(charなど)およびスコープなし列挙型のprvalueは、より大きな整数型(intなど)のprvalueに変換されることがあります。特に、算術演算子は、intより小さい型を引数として受け入れず、lvalue-to-rvalue変換後に整数昇格が自動的に適用されます(該当する場合)。この変換は常に値を保持します。

このセクションの次の暗黙の型変換は、整数昇格として分類されます。

特定のソース型に対して、整数昇格の宛先型は一意であり、他のすべての変換は昇格ではないことに注意してください。たとえば、オーバーロード解決は、charshort(変換)よりもcharint(昇格)を選択します。

[編集] 整数型からの昇格

bool型のprvalueは、int型のprvalueに変換できます。false0に、true1になります。

整数型Tboolを除く)のprvaluevalについて。

1) valビットフィールドに対するlvalue-to-rvalue変換の結果である場合。
  • intがビットフィールドのすべての値を表すことができる場合、valint型のprvalueに変換できます。
  • それ以外の場合、unsigned intがビットフィールドのすべての値を表すことができる場合、valunsigned intに変換できます。
  • それ以外の場合、valは項目(3)で指定された規則に従って変換できます。
2) それ以外の場合(valはビットフィールドから変換されていない)。
  • Tchar8_t(since C++20)char16_tchar32_tまたは(since C++11)wchar_tの場合、valは項目(3)で指定された規則に従って変換できます。
  • それ以外の場合、T整数変換ランクintのランクより低い場合。
  • intTのすべての値を表すことができる場合、valint型のprvalueに変換できます。
  • それ以外の場合、valunsigned int型のprvalueに変換できます。
3) 項目(1)(変換されたビットフィールドがunsigned intに収まらない場合)または項目(2)(Tが指定された文字型の一つである場合)で指定された場合、valは、その基底型(underlying type)のすべての値を表すことができる次の型リストの最初の型へのprvalueに変換できます。
  • int
  • unsigned int
  • long
  • unsigned long
  • long long
  • unsigned long long
  • Tの基底型。
(C++11以降)

[編集] 列挙型からの昇格

基底型が固定されていないスコープなし列挙型のprvalueは、その全値範囲を保持できる次のリストの最初の型へのprvalueに変換できます。

  • int
  • unsigned int
  • long
  • unsigned long
  • その整数変換ランクlong longのランクより大きい。
  • その整数変換ランクが、すべての拡張整数型の中で最も低い。
  • すべての拡張整数型の中で最も低い整数変換ランクを持つ2つの型がある場合、それは符号付き型である。
(C++11以降)


基底型が固定されたスコープなし列挙型のprvalueは、その基底型に変換できます。さらに、基底型が整数昇格の対象である場合、昇格された基底型に変換されます。非昇格の基底型への変換は、オーバーロード解決の目的ではより良いものです。

(C++11以降)

[編集] 浮動小数点昇格

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

この変換は浮動小数点昇格と呼ばれます。

[編集] 数値変換

昇格とは異なり、数値変換は値が変更される可能性があり、精度が失われる可能性があります。

[編集] 整数変換

整数型またはスコープなし列挙型のprvalueは、他の任意の整数型に変換できます。変換が整数昇格の下でリストされている場合、それは変換ではなく昇格です。

  • 宛先型が符号なしの場合、結果の値はソース値の剰余2n(ここでnは宛先型を表すために使用されるビット数)に等しい最小の符号なし値になります。
  • つまり、宛先型がより広いか狭いかに応じて、符号付き整数は符号拡張[1]または切り捨てられ、符号なし整数はそれぞれゼロ拡張または切り捨てられます。
  • 宛先型が符号付きの場合、ソース整数が宛先型で表せる場合、値は変更されません。それ以外の場合、結果は実装定義(until C++20)宛先型でソース値 modulo 2n (ここでnは宛先型を表すために使用されるビット数)に等しい一意の値(since C++20)になります(これは未定義の符号付き整数算術オーバーフローとは異なることに注意)。
  • ソース型がboolの場合、値falseはゼロに、値trueは宛先型の値1に変換されます(宛先型がintの場合、これは整数変換ではなく整数昇格であることに注意)。
  • 宛先型がboolの場合、これはブール変換(下記参照)です。
  1. これは、算術演算が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に変換されます。

[編集] ポインタ変換

ヌルポインタ定数は任意のポインタ型に変換でき、結果はその型のヌルポインタ値になります。このような変換(ヌルポインタ変換として知られる)は、cv修飾型への単一の変換として許可され、数値変換とcv修飾子変換の組み合わせとはみなされません。

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

  • 元のポインタがヌルポインタ値の場合、結果も宛先型のヌルポインタ値になります。

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

  • ptrがヌルポインタ値の場合、結果もヌルポインタ値になります。
  • それ以外の場合、BaseDerived仮想基底クラスであり、ptrDerivedに類似した型類似のオブジェクトを指しておらず、その生存期間内または構築・破棄期間内ではない場合、動作は未定義です。
  • それ以外の場合、結果は派生クラスオブジェクトの基底クラスサブオブジェクトへのポインタになります。

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

ヌルポインタ定数は任意のポインタメンバー型に変換でき、結果はその型のヌルメンバーポインタ値になります。このような変換(ヌルメンバーポインタ変換として知られる)は、cv修飾型への単一の変換として許可され、数値変換とcv修飾子変換の組み合わせとはみなされません。

Baseのメンバ(cv修飾されている可能性のある)Tへのポインタ」という型のprvalueは、「Derivedのメンバ(同様にcv修飾されている)Tへのポインタ」という型のprvalueに変換できます。ここで、BaseDerivedの基底クラスであり、Derivedは完全なクラス型です。Baseがアクセス不可能、曖昧、またはDerivedの仮想基底である場合、プログラムは不正な形式となります。

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

[編集] ブール変換

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

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

直接初期化のコンテキストでは、boolオブジェクトはstd::nullptr_t型(nullptrを含む)のprvalueから初期化されることがあります。結果の値はfalseです。ただし、これは暗黙の変換とはみなされません。

(C++11以降)

[編集] cv修飾子変換

一般的に。

  • cv修飾された型Tへのポインタ型のprvalueは、よりcv修飾された同じ型Tへのprvalueポインタに変換できます(つまり、constnessとvolatilityを追加できます)。
  • クラスXのcv修飾された型Tのメンバへのポインタ型のprvalueは、クラスXのよりcv修飾された型Tのメンバへのprvalueポインタに変換できます。

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

[編集] 類似型

非公式には、2つの型は、トップレベルのcv修飾を無視すると、

  • 同じ型である場合。
  • 両方ともポインタであり、ポインタ先が類似している場合。
  • 両方とも同じクラスのメンバへのポインタであり、ポインタ先メンバの型が類似している場合。
  • 両方とも配列であり、配列要素の型が類似している場合。

例:

  • 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>は類似していません。

正式には、型類似性はcv修飾分解(qualification-decomposition)によって定義されます。

Tcv修飾分解は、コンポーネントcv_iP_iのシーケンスであり、ここでTは非負のnに対して「cv_0 P_0 cv_1 P_1 ... cv_n−1 P_n−1 cv_n U」であり、

  • cv_iconstvolatileのセットであり、
  • P_i
  • 「ポインタ」
  • 「クラスC_iのメンバ(型は)」
  • 「Ni要素の配列」または
  • 「境界不明の配列」

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

// T is “pointer to pointer to const int”, it has 3 qualification-decompositions:
// n = 0 -> cv_0 is empty, U is “pointer to pointer to const int”
// n = 1 -> cv_0 is empty, P_0 is “pointer to”,
//          cv_1 is empty, U is “pointer to const int”
// n = 2 -> cv_0 is empty, P_0 is “pointer to”,
//          cv_1 is empty, P_1 is “pointer to”,
//          cv_2 is “const", U is “int”
using T = const int**;
 
// substitute any of the following type to U gives one of the decompositions:
// U = U0 -> the decomposition with n = 0: U0
// U = U1 -> the decomposition with n = 1: pointer to [U1]
// U = U2 -> the decomposition with n = 2: pointer to [pointer to [const U2]]
using U2 = int;
using U1 = const U2*;
using U0 = U1*;

2つの型T1T2は、それぞれに対してcv修飾分解が存在し、すべての次の条件が2つのcv修飾分解に対して満たされる場合に類似しています。

  • それらは同じnを持っています。
  • Uによって示される型は同じです。
  • 対応するP_iコンポーネントは同じですまたは、一方は「Ni要素の配列」で、もう一方は「境界不明の配列」です。(since C++20)、すべてのiに対して。
// the qualification-decomposition with n = 2:
// pointer to [volatile pointer to [const int]]
using T1 = const int* volatile *;
 
// the qualification-decomposition with n = 2:
// const pointer to [pointer to [int]]
using T2 = int** const;
 
// For the two qualification-decompositions above
// although cv_0, cv_1 and cv_2 are all different,
// they have the same n, U, P_0 and P_1,
// therefore types T1 and T2 are similar.

[編集] cv修飾子の結合

以下の説明では、型Tnの最も長いcv修飾分解はDnと示され、そのコンポーネントはcvn_iPn_iと示されます。

T1のprvalue式は、すべての次の条件が満たされる場合に型T2に変換できます。

  • T1T2は類似しています。
  • すべての非ゼロiについて、cv1_iconstが含まれる場合、cv2_iにもconstが含まれます。volatileについても同様です。
  • すべての非ゼロiについて、cv1_icv2_iが異なる場合、i1からi-1まで)のすべてのkについて、cv2_kconstが追加されます。

2つの型T1T2cv修飾結合型は、T1に類似した型T3であり、

  • cv3_0は空です。
  • すべての非ゼロiについて、cv3_icv1_icv2_iの和集合です。
  • cv3_icv1_iまたはc2_iと異なる場合、またはi1からi-1まで)のすべてのkについて、cv3_kconstが追加されます。
(C++20まで)

2つの型T1T2cv修飾結合型は、T1に類似した型T3であり、D3は次のすべての条件を満たします。

  • cv3_0は空です。
  • すべての非ゼロiについて、cv3_icv1_icv2_iの和集合です。
  • P1_iまたはP2_iが「境界不明の配列」の場合、P3_iは「境界不明の配列」になります。それ以外の場合はP1_iです。
  • cv3_icv1_iまたはcv2_iと異なる場合、またはP3_iP1_iまたはP2_iと異なる場合、i1からi-1まで)のすべてのkについて、cv3_kconstが追加されます。

T1のprvalueは、T1T2のcv修飾結合型がcv修飾されていないT2である場合に、型T2に変換できます。

(C++20以降)
// longest qualification-decomposition of T1 (n = 2):
// pointer to [pointer to [char]]
using T1 = char**;
 
// longest qualification-decomposition of T2 (n = 2):
// pointer to [pointer to [const char]]
using T2 = const char**;
 
// Determining the cv3_i and T_i components of D3 (n = 2):
// cv3_1 = empty (union of empty cv1_1 and empty cv2_1)
// cv3_2 = “const” (union of empty cv1_2 and “const” cv2_2)
// P3_0 = “pointer to” (no array of unknown bound, use P1_0)
// P3_1 = “pointer to” (no array of unknown bound, use P1_1)
// All components except cv_2 are the same, cv3_2 is different from cv1_2,
// therefore add “const” to cv3_k for each k in [1, 2): cv3_1 becomes “const”.
// T3 is “pointer to const pointer to const char”, i.e., const char* const *.
using T3 = /* the qualification-combined type of T1 and T2 */;
 
int main()
{
    const char c = 'c';
    char* pc;
    T1 ppc = &pc;
    T2 pcc = ppc; // Error: T3 is not the same as cv-unqualified T2,
                  //        no implicit conversion.
 
    *pcc = &c;
    *pc = 'C';    // If the erroneous assignment above is allowed,
                  // the const object “c” may be modified.
}

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

char** p = 0;
char * const* p1 = p;       // OK in C and C++
const char* const * p2 = p; // error in C, OK in 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;)がある場合、その関数呼び出しの後に追加の標準変換シーケンスが1つ許可されたため、結果のboolintに変換でき、obj << 1;int i = obj;のようなコードが可能でした。

これに対する初期の解決策の1つは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_iosLWG issue 468を通じてこのイディオムを許可し、operator void*は(を参照)置き換えられました。

C++11以降、明示的な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 未初期化オブジェクトおよびポインタオブジェクトの
lvalueからrvalueへの変換の
無効な値の動作は常に未定義でした。
未定義のunsigned char
は許可されました。無効なポインタの使用は
実装定義です。
CWG 685 C++98 列挙型の基底型は
基底型が固定されている場合、整数昇格で優先されませんでした。
優先されるようになりました。
CWG 707 C++98 整数から浮動小数点数への変換
すべての場合で定義された動作でした。
未定義の動作は
変換される値が
宛先範囲外の場合です。
CWG 1423 C++11 std::nullptr_tboolに変換可能でした。
直接初期化とコピー初期化の両方で。
直接初期化のみで。
CWG 1773 C++11 名前式が評価対象
式に出現し、名前付けされたオブジェクトがODR使用されない場合、lvalue-to-rvalue変換中に
依然として評価される可能性がありました。
評価されなくなりました。
CWG 1781 C++11 std::nullptr_tからboolへの変換は暗黙の
変換とみなされていましたが、直接初期化でのみ有効でした。
暗黙の変換とはみなされなくなりました。
暗黙の変換ではありません。
CWG 1787 C++98 未定義の
unsigned charのレジスタにキャッシュされた
符号ビットにシフトする動作は定義された
値表現からの読み取りの動作は未定義でした。 C++11 CWG 1981 考慮されなかった
コンテキスト依存の変換は、明示的な変換関数を考慮していました。 C++11 CWG 2140
lvalue-to-rvalue変換が
std::nullptr_t lvalueからメモリをフェッチするかどうかは不明でした。
フェッチされません。 C++98 CWG 2310
派生クラスから基底クラスへのポインタ変換および
基底クラスから派生クラスへのポインタメンバー変換において、
派生クラス型が不完全である可能性がありました。
完全である必要があります。 C++20 CWG 2484
char8_tchar16_tは異なる整数
昇格戦略を持っていましたが、両方とも適合します。
char8_tは、char16_tと同じ方法で昇格されるべきです。
CWG 2485 C++98 ビットフィールドを含む整数昇格の仕様が不明確でした。 仕様が改善されました。
CWG 2813 C++23 クラスprvalueの明示的な
メンバ関数が呼び出されたときに、一時オブジェクトの生成が発生しました。
発生しません。
この場合に
CWG 2861 C++98 型にアクセスできないオブジェクトへのポインタが
基底クラスサブオブジェクトへのポインタに変換される可能性がありました。
この場合の動作は
未定義となる
CWG 2879 C++17 一時オブジェクトの生成変換がprvalueに適用されました。
glvalueを期待する演算子のオペランドとして。
一部のケースでは適用されませんでした。
CWG 2899 C++98 lvalue-to-rvalue変換は、lvalueに適用される可能性がありました。
無効な値表現を持つオブジェクトを指定する。
この場合の動作は
未定義となる
CWG 2901 C++98 intオブジェクトを参照するunsigned int lvalueからのlvalue-to-rvalue変換の結果。
値が-1の場合、不明確でした。
明確化された

[編集] 関連項目

Cドキュメント Implicit conversions
English 日本語 中文(简体) 中文(繁體)