標準変換

C++ 言語では、基本型間での変換が定義されています。 また、ポインター、参照、およびメンバーへのポインターの派生型についても変換が定義されています。 これらの変換は、標準変換 と呼ばれる。

このセクションでは、次の標準変換について説明します。

  • 整数の上位変換

  • 整数の変換

  • 浮動小数点の変換

  • 浮動小数点と整数の変換

  • 算術変換

  • ポインター変換

  • 参照変換

  • メンバーへのポインター変換

    注意

    ユーザー定義型では独自の変換を指定できます。 ユーザー定義型の変換については、「コンストラクターと変換を参照してください

次のコードは、変換 (この例では整数の上位変換) の実行例を示しています。

long  long_num1, long_num2;
int   int_num;

// int_num promoted to type long prior to assignment.
long_num1 = int_num;

// int_num promoted to type long prior to multiplication.
long_num2 = int_num * long_num2;

参照型を生成する場合のみ、変換の結果が左辺値になります。 たとえば、 として宣言されたユーザー定義変換は参照 operator int&() を返し、 は l 値です。 ただし、 として宣言された変換は operator int() オブジェクトを返し、l 値ではありません。

整数の上位変換

整数型のオブジェクトは、別のより広い整数型 、つまり、より大きな値のセットを表す型に変換できます。 この拡大変換の型は、"整数の昇格 " と呼ばれる。 整数の昇格では、別の整数型を使用できる場合は、式で次の型を使用できます。

  • および 型のオブジェクト、リテラル char 、定数 short int

  • 列挙型

  • int ビット フィールド

  • 列挙子

昇格後の値が昇格前の値と同じと保証されるので、C++ の昇格は "値を保持" します。 値保持の昇格では、短い整数型のオブジェクト (ビット フィールドや型のオブジェクトなど) は、元の型の全範囲を表す場合に型に昇格 char int int されます。 が int 値の範囲全体を表できない場合、オブジェクトは 型に昇格されます unsigned int 。 この方法は Standard C で使用される方法と同じですが、値を保持する変換では、 オブジェクトの "署名" は保持されません。

値保持の上位変換および符号の有無を保持する上位変換は、通常、同じ結果を生成します。 ただし、昇格されたオブジェクトが次のように表示される場合は、異なる結果が生成される可能性があります。

  • / % /= 、、、、、、 %= < または <= > のオペランド>=

    これらの演算子は、結果を判断するために符号に依存します。 これらのオペランドに適用すると、値の保持と署名保持の昇格によって異なる結果が生成されます。

  • または の左 >> オペランド >>=

    これらの演算子は、シフト操作で符号付き数量と符号なし数量を異なる方法で処理します。 符号付き数量の場合、右シフト操作では符号ビットが空いたビット位置に伝達され、空いたビット位置は符号なし数量でゼロで埋め込されます。

  • オーバーロードされた関数の引数、またはオーバーロードされた演算子のオペランド。これは、引数の一致に対するオペランド型の符号付き性に依存します。 オーバーロードされた演算子の定義の詳細については、「オーバーロードされた演算子」 を参照してください

整数の変換

整数変換は、 整数型間の変換です。 整数型は char short 、、(または short int int long )、、、および です long long 。 これらの型は、 または で修飾 signed できます。 unsigned また unsigned 、 の shorthand として使用できます unsigned int

signed から unsigned へ

符号付き整数型のオブジェクトは、対応する符号なし型に変換できます。 これらの変換が発生しても、実際のビット パターンは変更されません。 ただし、データの解釈は変わります。 次のコードを考えてみます。

#include <iostream>

using namespace std;
int main()
{
    short  i = -3;
    unsigned short u;

    cout << (u = i) << "\n";
}
// Output: 65533

前の例では、 が signed short i 定義され、負の数値に初期化されます。 式は (u = i) 、 への i 割り当ての前に unsigned short に変換されます u

unsigned から signed へ

符号なし整数型のオブジェクトは、対応する符号付き型に変換できます。 ただし、符号なし値が符号付き型の表現可能な範囲を外している場合、次の例に示す正しい値は結果にはありません。

#include <iostream>

using namespace std;
int main()
{
short  i;
unsigned short u = 65533;

cout << (i = u) << "\n";
}
//Output: -3

前の例では、 は、式 を評価するために符号付き数量に変換 u unsigned short する必要がある整数オブジェクトです (i = u) 。 その値を で適切に表すのはできないので、データは次 signed short のように誤って解釈されます。

浮動小数点の変換

浮動小数点型のオブジェクトは、より精度の高い浮動小数点型に安全に変換できます。つまり、変換で精度は低下しません。 たとえば、 から への変換または から への変換は float double 安全であり double long double 、値は変更されません。

浮動小数点型のオブジェクトは、その型で表される範囲内にある場合は、より正確な型に変換できます。 (浮動小数点 型の範囲については、 浮動小数点制限に関するページを参照してください)。元の値が正確に表現できない場合は、次の高い値または次に低い表現可能な値に変換できます。 このような値が存在しない場合、結果は未定義になります。 次の例を確認してください。

cout << (float)1E300 << endl;

型で表される最大値は float 3.402823466E38 です。1E300 よりはるかに小さい数値です。 したがって、数値は無限大に変換され、結果は "inf" になります。

整数型と浮動小数点型の変換

特定の式は、浮動小数点型のオブジェクトから整数型のオブジェクトへの変換、またはその逆の変換が発生する場合があります。 整数型のオブジェクトが浮動小数点型に変換され、元の値が正確に表現できない場合、結果は次の上位または次の下位表現可能な値になります。

浮動小数点型のオブジェクトが整数型に変換される場合、小数部は切り捨てられるか、ゼロに丸められます。 1.3 のような数値は 1 に変換され、-1.3 は -1 に変換されます。 切り捨てられた値が最も高い表現可能な値より大きい場合、または最も低い表現可能な値より小さい場合、結果は未定義になります。

算術変換

多くの二項演算子 (「二項演算子を使用した 」で説明) は、オペランドの変換を引き起こし、結果を同じように生成します。 これらの演算子によって発生する変換は、通常の算 術変換 と呼ばれる。 ネイティブ型が異なるオペランドの算術変換は、次の表に示すように行われます。 typedef 型は、基になるネイティブ型に従って動作します。

型変換の条件

満たされる条件 変換
どちらのオペランドも 型です long double その他のオペランドは 型に変換されます long double
前の条件が満たされていない場合、どちらのオペランドも 型です double その他のオペランドは 型に変換されます double
前の条件が満たされていないので、どちらのオペランドも 型です float その他のオペランドは 型に変換されます float
上の条件が満たされていない (どちらのオペランドも浮動小数点型ではない) オペランドは、次のように整数の昇格を取得します。

- いずれかのオペランドが 型の場合 unsigned long 、もう一方のオペランドは 型に変換されます unsigned long
- 上記の条件が満たされていない場合、どちらかのオペランドが 型で、もう 1 つのオペランドが 型の場合、両方のオペランド long unsigned int が 型に変換されます unsigned long
- 上記の 2 つの条件が満たされていない場合、いずれかのオペランドが 型の場合、もう一方のオペランド long は 型に変換されます long
- 上記の 3 つの条件が満たされていない場合、いずれかのオペランドが 型の場合、もう一方のオペランドは 型 unsigned int に変換されます unsigned int
- 上記の条件が満たされなき場合、両方のオペランドが 型に変換されます int

次のコードは、表で説明している変換規則を示しています。

double dVal;
float fVal;
int iVal;
unsigned long ulVal;

int main() {
   // iVal converted to unsigned long
   // result of multiplication converted to double
   dVal = iVal * ulVal;

   // ulVal converted to float
   // result of addition converted to double
   dVal = ulVal + fVal;
}

上記の例の最初のステートメントは、2 つの整数型、iValulVal の乗算を示しています。 条件が満たされるのは、どちらのオペランドも浮動小数点型でもなく、1 つのオペランドが 型であるという条件です unsigned int 。 そのため、もう 1 つのオペランド iVal である は 型に変換されます unsigned int 。 その後、結果が に割り当てられます dVal 。 ここで満たされる条件は、1 つのオペランドが 型なので、乗算の結果は 型 double unsigned int に変換されます double

前の例の 2 番目のステートメントは、 と 整数型 と の float 加算を示 fVal しています ulVal 。 変数 ulVal は 型 (テーブルの float 3 番目の条件) に変換されます。 加算の結果は 型 (テーブル内の 2 番目の条件) に変換され double 、 に割り当てられます dVal

ポインター変換

ポインターは、代入、初期化、比較、および他の式の中で変換できます。

クラスへのポインター

クラスへのポインターを基底クラスへのポインターに変換できる 2 つのケースがあります。

最初のケースは、指定した基底クラスがアクセス可能であり、変換が明確である場合です。 あいまいな基本クラス参照の詳細については、「複数の基本クラス」 を参照してください

基底クラスにアクセスできるかどうかは、派生で使用される継承の種類によって決まります。 次の図に示す継承を考えます。

基本クラスのアクセシビリティを示す継承グラフ。
基底クラスのアクセシビリティを示す継承グラフ

次の表は、図で示す状況に対する基底クラスのアクセシビリティを示します。

関数の型 派生 変換 (

B* から A * の法律
外部 (非クラス スコープ) 関数 プライベート いいえ
Protected いいえ
パブリック Yes
B のメンバー関数 (B のスコープ内) プライベート Yes
Protected Yes
パブリック Yes
C のメンバー関数 (C のスコープ内) プライベート いいえ
Protected Yes
パブリック Yes

クラスへのポインターを基底クラスへのポインターに変換できる 2 番目のケースは、明示的な型変換を使用する場合です 明示的な型変換の詳細については、「明示的な型変換 演算子」を参照してください

このような変換の結果は、サブオブジェクト へのポインター です。これは、基本クラスによって完全に記述される オブジェクトの部分です。

次のコードでは、2 つのクラス AB を定義しています。BA から派生しています。 (継承の詳細については、「派生クラス 」を参照してください)。次に、 型の オブジェクトと、 オブジェクトを指す 2 つのポインター ( と bObject B ) pA pB を定義します。

// C2039 expected
class A
{
public:
    int AComponent;
    int AMemberFunc();
};

class B : public A
{
public:
    int BComponent;
    int BMemberFunc();
};
int main()
{
   B bObject;
   A *pA = &bObject;
   B *pB = &bObject;

   pA->AMemberFunc();   // OK in class A
   pB->AMemberFunc();   // OK: inherited from class A
   pA->BMemberFunc();   // Error: not in class A
}

ポインター pA は、型 A * へのポインターを意味するものとして解釈できる型 A です。 bObjectのメンバー ( や BComponent BMemberFunc など) は型に固有であるため、 B を介してアクセスできません pApA ポインターは、クラス A で定義されているオブジェクトのこれらの特性 (メンバー関数とデータ) にのみアクセスを許可します。

関数へのポインター

型がポインターを保持するのに十分な大きい場合は、関数へのポインターを 型 void * void * に変換できます。

void へのポインター

型へのポインターは、他の型へのポインターに変換できますが、明示的な型キャスト void でのみ変換できます (C の場合とは異なります)。 任意の型へのポインターは、 型へのポインターに暗黙的に変換できます void 。 型の不完全なオブジェクトへのポインターは、(暗黙的に) および戻る void (明示的に) ポインターに変換できます。 このような変換の結果は、元のポインターの値と同じです。 オブジェクトが宣言されている場合、オブジェクトは不完全と見なされますが、そのサイズまたは基本クラスを決定するために使用できる情報が不足しています。

ではない、または型のポインターに暗黙的に変換できる任意のオブジェクト const volatile へのポインター void *

const ポインターと volatile ポインター

C++ では、 または 型から または ではない型への標準の変換 const volatile は提供 const されない volatile 。 ただし、どの種類の変換も、明示的な型キャストを使用して指定できます (安全でない変換も含む)。

注意

静的メンバーへのポインターを除き、メンバーへの C++ ポインターは通常のポインターとは異なり、同じ標準変換はありません。 静的メンバーへのポインターは通常のポインターであり、通常のポインターと同じ変換を持ちます。

null ポインターの変換

0 に評価される整数定数式、またはポインター型にキャストされる式は、null ポインター と呼ばれるポインター に変換されます。 このポインターは常に、有効なオブジェクトまたは関数へのポインターと等しくないと比較します。 例外は、同じオフセットを持ち、引き続き異なるオブジェクトを指す、ベースのオブジェクトへのポインターです。

C++11 では 、C スタイルの null ポインターより nullptr 型を優先する必要があります。

ポインター式の変換

配列型の式は、同じ型のポインターに変換できます。 変換の結果は最初の配列要素へのポインターです。 次のコードは、この変換を示す例です。

char szPath[_MAX_PATH]; // Array of type char.
char *pszPath = szPath; // Equals &szPath[0].

特定の型を返す関数となる式は、次の場合を除き、その型を返す関数へのポインターに変換されます。

  • 式は、address-of 演算子 ( ) のオペランドとして使用されます &

  • 式が関数呼び出し演算子のオペランドとして使用されています。

参照変換

クラスへの参照は、次の場合に基本クラスへの参照に変換できます。

  • 指定した基本クラスにアクセスできます。

  • 変換は明確です。 (あいまいな基本クラス参照の詳細については、「複数の基本クラス」 を参照してください)。

変換の結果は、基底クラスを表すサブオブジェクトへのポインターです。

メンバーへのポインター

クラス メンバーへのポインターは、代入、初期化、比較、および他の式の中で変換できます。 このセクションでは、以下のポインターからメンバーへの変換について説明します。

基底クラスのメンバーへのポインター

基底クラスのメンバーへのポインターは、次の条件を満たす場合に、そのクラスから派生したクラスのメンバーへのポインターに変換できます。

  • 派生クラスへのポインターから基底クラスへのポインターへの逆変換がアクセス可能である。

  • 派生クラスが基底クラスからの仮想的な継承でない。

左のオペランドがメンバーへのポインターである場合、右のオペランドはメンバーへのポインター型であるか、0 に評価される定数式である必要があります。 この代入は、次の場合にのみ有効です。

  • 右のオペランドが、左のオペランドと同じクラスのメンバーへのポインターである。

  • 左のオペランドが、右のオペランドのクラスからパブリックかつ明確に派生したクラスのメンバーへのポインターである。

メンバー変換への null ポインター

0 に評価される整数定数式は、null ポインターに変換されます。 このポインターは常に、有効なオブジェクトまたは関数へのポインターと等しくないと比較します。 例外は、同じオフセットを持ち、引き続き異なるオブジェクトを指す、ベースのオブジェクトへのポインターです。

次のコードは、クラス i のメンバー A へのポインターの定義を示しています。 ポインター pai が 0、つまり null ポインターに初期化されます。

class A
{
public:
int i;
};

int A::*pai = 0;

int main()
{
}

こちらもご覧ください

C++ 言語リファレンス