標準変換

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&() 参照を返し、は左辺値です。 ただし、として宣言された変換は operator int() オブジェクトを返し、左辺値ではありません。

整数の上位変換

整数型のオブジェクトは、より大きな整数型、つまり、より大きな値のセットを表すことができる型に変換できます。 この拡大型の変換は、 整数の上位変換と呼ばれます。 整数の上位変換では、別の整数型を使用できる場所であれば、式で次の型を使用できます。

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

  • 列挙型

  • int ビットフィールド

  • 列挙子

C++ の昇格は、昇格後の値が上位変換前の値と同じであることが保証されるため、"値を保持する" となります。 値を保持する昇格では、 charintint が元の型の全範囲を表すことができる場合は、短い整数型 (ビットフィールドや型のオブジェクトなど) のオブジェクトが型に昇格されます。 が int 値の全範囲を表すことができない場合、オブジェクトは型に昇格され unsigned int ます。 この方法は、標準 C で使用される方法と同じですが、値を維持する変換では、オブジェクトの "有無" が保持されません。

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

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

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

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

    これらの演算子は、シフト演算で符号付きと符号なしの数量を別々に扱います。 符号付きの数量の場合、右シフト演算では符号ビットが空いているビット位置に伝達されますが、空いたビット位置は符号なしの数量でゼロで埋められます。

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

整数の変換

整数変換 は、整数型の間の変換です。 整数型は char 、、 short (または short int )、、 intlong 、および long long です。 これらの型は、またはで修飾することができ、 signedunsignedunsigned の短縮形として使用でき unsigned int ます。

signed から unsigned へ

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

#include <iostream>

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

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

前の例では、 signed shorti が定義され、負の数に初期化されています。 式は、が (u = i)i に代入される前に、をに変換し unsigned shortu ます。

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 またはからへの変換 doublelong double は安全であり、値は変更されません。

また、浮動小数点型のオブジェクトは、その型で表現できる範囲内にある場合は、より精度の低い型に変換することもできます。 (浮動小数点型の範囲については、「 浮動小数点値の制限 」を参照してください)。 元の値が正確に表現できない場合は、次に大きい値または次に小さい表現可能値のいずれかに変換できます。 そのような値が存在しない場合、結果は未定義になります。 次の例を考えてみましょう。

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

型によって表現可能 float な最大値は、 — 1e300 よりもはるかに小さい 3.402823466 e38 です。 そのため、数値は無限大に変換され、結果は "inf" になります。

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

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

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

算術変換

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

型変換の条件

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

-どちらかのオペランドが型の場合 unsigned long 、もう一方のオペランドは型に変換され unsigned long ます。
-前の条件が満たされず、どちらかのオペランドが型で、もう一方が型である場合 longunsigned 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 。 そのため、もう一方のオペランドは iVal 型に変換され unsigned int ます。 結果はに割り当てられ dVal ます。 ここで条件を満たすのは、1つのオペランドが型 double であるため、 unsigned int 乗算の結果が型に変換されるためです double

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

ポインター変換

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

クラスへのポインター

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

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

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

Inheritance graph showing base class accessibility.
基底クラスのアクセシビリティを示す継承グラフ

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

関数の型 派生 変換 (

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

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

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

次のコードでは、2 つのクラス AB を定義しています。BA から派生しています。 (継承の詳細については、「 派生クラス」を参照してください)。 次に bObject 、、型のオブジェクト、および BpA オブジェクトを指す2つのポインター (と) を定義し 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 (やなど BComponentBMemberFunc ) は型に固有であるため、を使用してアクセスすることはできませ BpA ん。 pA ポインターは、クラス A で定義されているオブジェクトのこれらの特性 (メンバー関数とデータ) にのみアクセスを許可します。

関数へのポインター

void *void * がそのポインターを保持するのに十分な大きさであれば、関数へのポインターを型に変換できます。

void へのポインター

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

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

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

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

注意

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

null ポインターの変換

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

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

ポインター式の変換

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

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

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

  • 式は、アドレス演算子 () のオペランドとして使用され & ます。

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

参照変換

このような場合は、クラスへの参照を基底クラスへの参照に変換できます。

  • 指定された基底クラスにアクセスできます。

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

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

メンバーへのポインター

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

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

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

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

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

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

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

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

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

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

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

class A
{
public:
int i;
};

int A::*pai = 0;

int main()
{
}

こちらもご覧ください

C++ 言語リファレンス