型変換とタイプ セーフType conversions and type safety

このドキュメントでは、共通型の変換に伴う問題を識別し、C++ コードでそれらを回避する方法を説明します。This document identifies common type conversion problems and describes how you can avoid them in your C++ code.

C ++.のプログラムを作成するときは、そのプログラムがタイプ セーフであることを確認することが重要です。When you write a C++ program, it's important to ensure that it's type-safe. これは、すべての変数、関数の引数、および関数の戻り値が適切な種類のデータを格納すること、異なる型の値が関係する操作が "意味のある" 処理を行うこと、さらにデータ損失、ビット パターンの誤った解釈、メモリ破損を発生させないことを意味します。This means that every variable, function argument, and function return value is storing an acceptable kind of data, and that operations that involve values of different types "make sense" and don't cause data loss, incorrect interpretation of bit patterns, or memory corruption. プログラムが、明示的または暗黙的に、ある型の値を他の型に変換しないことが、定義上のタイプ セーフです。A program that never explicitly or implicitly converts values from one type to another is type-safe by definition. ただし、型の変換、また場合によっては安全でない変換が必須になることもあります。However, type conversions, even unsafe conversions, are sometimes required. たとえば、浮動小数点演算の結果を型の変数に格納する必要がある場合 int や、 unsigned int を受け取る関数にの値を渡す必要がある場合があり signed int ます。For example, you might have to store the result of a floating point operation in a variable of type int, or you might have to pass the value in an unsigned int to a function that takes a signed int. どちらの例も、データ損失や値の再解釈を引き起こす可能性があるため、安全ではない変換を示していると言えます。Both examples illustrate unsafe conversions because they may cause data loss or re-interpretation of a value.

コンパイラが安全ではない変換を検出した場合は、エラーまたは警告を発行します。When the compiler detects an unsafe conversion, it issues either an error or a warning. エラーが生じた場合はコンパイルが中止されます。警告が生じた場合は、コンパイルは続行されますが、コード内にエラーが存在する可能性が示されます。An error stops compilation; a warning allows compilation to continue but indicates a possible error in the code. ただし、警告なしでプログラムがコンパイルされた場合でも、正しくない結果を生成する暗黙の型変換につながるコードが存在している可能性があります。However, even if your program compiles without warnings, it still may contain code that leads to implicit type conversions that produce incorrect results. 型エラーは、コード内の明示的な変換、またはキャストによって発生する可能性があります。Type errors can also be introduced by explicit conversions, or casts, in the code.

暗黙的な型変換Implicit type conversions

式に異なる組み込み型のオペランドが含まれており、明示的なキャストが存在しない場合、コンパイラは、組み込みの 標準変換 を使用してオペランドの1つを変換し、型が一致するようにします。When an expression contains operands of different built-in types, and no explicit casts are present, the compiler uses built-in standard conversions to convert one of the operands so that the types match. コンパイラはいずれかが成功するまで、適切に定義された一連の変換を試みます。The compiler tries the conversions in a well-defined sequence until one succeeds. 選択した変換が上位変換である場合は、コンパイラは警告を発行しません。If the selected conversion is a promotion, the compiler does not issue a warning. 変換が縮小変換である場合は、コンパイラは、データ損失の可能性に関する警告を発行します。If the conversion is a narrowing, the compiler issues a warning about possible data loss. 実際のデータ損失が発生するかどうかは、関与する実際の値に依存しますが、この警告をエラーとして扱うことをお勧めします。Whether actual data loss occurs depends on the actual values involved, but we recommend that you treat this warning as an error. ユーザー定義型が関与している場合は、コンパイラは、ユーザーがクラス定義の中で指定した変換を使用しようとします。If a user-defined type is involved, then the compiler tries to use the conversions that you have specified in the class definition. 受け入れ可能な変換が見つからない場合は、コンパイラはエラーを発行し、プログラムをコンパイルしません。If it can't find an acceptable conversion, the compiler issues an error and does not compile the program. 標準変換を制御する規則の詳細については、「 標準変換」を参照してください。For more information about the rules that govern the standard conversions, see Standard Conversions. ユーザー定義変換の詳細については、「 ユーザー定義変換 (C++/cli)」を参照してください。For more information about user-defined conversions, see User-Defined Conversions (C++/CLI).

拡大変換 (上位変換)Widening conversions (promotion)

拡大変換では、より小さい変数内の値が、より大きい変数に代入され、データは失われません。In a widening conversion, a value in a smaller variable is assigned to a larger variable with no loss of data. 拡大変換は常に安全であるため、コンパイラはこの変換を自動的に実行し、警告を発行しません。Because widening conversions are always safe, the compiler performs them silently and does not issue warnings. 次の変換は、拡大変換です。The following conversions are widening conversions.

差出人From 終了To
またはを signed 除く任意または unsigned 整数型 long long****__int64Any signed or unsigned integral type except long long or __int64 double
bool または charbool or char 他のすべての組み込み型Any other built-in type
short または wchar_tshort or wchar_t int, long, long longint, long, long long
int, longint, long long long
float double

縮小変換 (強制型)Narrowing conversions (coercion)

コンパイラは、暗黙的に縮小変換を実行しますが、データ損失の可能性に関する警告を発行します。The compiler performs narrowing conversions implicitly, but it warns you about potential data loss. これらの警告を重大なものとして受け止めてください。Take these warnings very seriously. 大きい変数の中にある値が、小さい変数の中に必ず収容でき、データ損失が発生しないことが確実な場合は、コンパイラが今後警告を発行しないように、明示的なキャストを追加してください。If you are certain that no data loss will occur because the values in the larger variable will always fit in the smaller variable, then add an explicit cast so that the compiler will no longer issue a warning. 変換が安全かどうか自信がない場合は、コード内になんらかの種類の実行時チェックを追加し、発生する可能性のあるデータ損失に対処できるようにしてください。その結果、プログラムが誤った結果を生成することはありません。If you are not sure that the conversion is safe, add to your code some kind of runtime check to handle possible data loss so that it does not cause your program to produce incorrect results.

浮動小数点型から整数型へのあらゆる変換は、浮動小数点値の小数部が破棄されて失われるため、縮小変換です。Any conversion from a floating point type to an integral type is a narrowing conversion because the fractional portion of the floating point value is discarded and lost.

次のコード例では、いくつかの暗黙的な縮小変換と、それらに関してコンパイラが発行する警告を示します。The following code example shows some implicit narrowing conversions, and the warnings that the compiler issues for them.

int i = INT_MAX + 1; //warning C4307:'+':integral constant overflow
wchar_t wch = 'A'; //OK
char c = wch; // warning C4244:'initializing':conversion from 'wchar_t'
              // to 'char', possible loss of data
unsigned char c2 = 0xfffe; //warning C4305:'initializing':truncation from
                           // 'int' to 'unsigned char'
int j = 1.9f; // warning C4244:'initializing':conversion from 'float' to
              // 'int', possible loss of data
int k = 7.7; // warning C4244:'initializing':conversion from 'double' to
             // 'int', possible loss of data

符号付き - 符号なしの変換Signed - unsigned conversions

符号付き整数型と符号なし整数型は常に同じサイズですが、値変換の目的でビット パターンを解釈する方法が異なります。A signed integral type and its unsigned counterpart are always the same size, but they differ in how the bit pattern is interpreted for value transformation. 次のコード例は、同じビット パターンが符号付きの値、および符号なしの値として解釈されるときの動作を示します。The following code example demonstrates what happens when the same bit pattern is interpreted as a signed value and as an unsigned value. numnum2 の両方に格納されているビット パターンは、前の図から決して変化していません。The bit pattern stored in both num and num2 never changes from what is shown in the earlier illustration.

using namespace std;
unsigned short num = numeric_limits<unsigned short>::max(); // #include <limits>
short num2 = num;
cout << "unsigned val = " << num << " signed val = " << num2 << endl;
// Prints: unsigned val = 65535 signed val = -1

// Go the other way.
num2 = -1;
num = num2;
cout << "unsigned val = " << num << " signed val = " << num2 << endl;
// Prints: unsigned val = 65535 signed val = -1

値の再解釈が双方向で発生することに注意してください。 期待される値に対して、符号が反転したように思える奇妙な結果をプログラムが返した場合は、符号付き整数型と符号なし整数型の間で暗黙的な変換が実行された可能性に注目してください。 次の例では、式 (0-1) の結果が int unsigned int に格納されている場合、からに暗黙的に変換され num ます。 この結果、ビット パターンの再解釈が発生します。This causes the bit pattern to be reinterpreted.

unsigned int u3 = 0 - 1;
cout << u3 << endl; // prints 4294967295

符号付きと符号なし整数型の間の暗黙的な変換について、コンパイラは警告を発行しません。The compiler does not warn about implicit conversions between signed and unsigned integral types. したがって、符号付きから符号なしへの変換を決して実施しないことをお勧めします。Therefore, we recommend that you avoid signed-to-unsigned conversions altogether. このような変換を回避できない場合は、変換されようとしている値が 0 以上であるかどうか、および符号付きの型の最大値以下であるかどうかを検出する実行時チェックをコードに追加してください。If you can't avoid them, then add to your code a runtime check to detect whether the value being converted is greater than or equal to zero and less than or equal to the maximum value of the signed type. この範囲内にある値は、再解釈なしで、符号付きから符号なし、および符号なしから符号付きへと変換されます。Values in this range will transfer from signed to unsigned or from unsigned to signed without being reinterpreted.

ポインター変換Pointer conversions

多くの式では、C 形式の配列は、配列内の最初の要素へのポインターに暗黙的に変換され、また定数変換が警告なしで実施される可能性があります。In many expressions, a C-style array is implicitly converted to a pointer to the first element in the array, and constant conversions can happen silently. これは便利ですが、場合によってはエラーが発生しやすくなります。Although this is convenient, it's also potentially error-prone. たとえば、不適切に設計された次のコード例は、無意味のように見えますが、コンパイルして ' p ' の結果を生成します。For example, the following badly designed code example seems nonsensical, and yet it will compile and produces a result of 'p'. まず、"Help" 文字列定数リテラルは、 char* 配列の最初の要素を指すに変換されます。このポインターは、最後の要素 ' p ' を指すように、3つの要素によってインクリメントされます。First, the "Help" string constant literal is converted to a char* that points to the first element of the array; that pointer is then incremented by three elements so that it now points to the last element 'p'.

char* s = "Help" + 3;

明示的な変換 (キャスト)Explicit conversions (casts)

キャスト操作を使用すると、ある型の値を別の型に変換するようにコンパイラに指示できます。By using a cast operation, you can instruct the compiler to convert a value of one type to another type. 2 つの方が完全に無関係である場合はコンパイラはエラーを生成しますが、操作がタイプ セーフでない場合でもエラーを生成しない状況が存在します。The compiler will raise an error in some cases if the two types are completely unrelated, but in other cases it will not raise an error even if the operation is not type-safe. キャストは控えめに使用してください。ある型から別の型への変換は、プログラム エラーの原因となる可能性があるためです。Use casts sparingly because any conversion from one type to another is a potential source of program error. ただし、時にはキャストが必須であり、すべてのキャストが等しく危険というわけでもありません。However, casts are sometimes required, and not all casts are equally dangerous. キャストの 1 つの効率的な使用方法は、コードが縮小変換を実行し、その変換により、プログラムが不適切な結果をもたらさないことがわかっている場合です。One effective use of a cast is when your code performs a narrowing conversion and you know that the conversion is not causing your program to produce incorrect results. 実際には、何を実行するかを開発者が理解していて、その点に関する警告を発行しないようにコンパイラに指示することになります。In effect, this tells the compiler that you know what you are doing and to stop bothering you with warnings about it. 別の使用法は、ポインターから派生クラス、およびポインターから基底クラスへのキャストです。Another use is to cast from a pointer-to-derived class to a pointer-to-base class. もう1つの用途は、変数の値をキャストして、 const 非引数を必要とする関数に渡すことです constAnother use is to cast away the const-ness of a variable to pass it to a function that requires a non-const argument. これらのキャスト操作のほとんどには、ある程度のリスクが存在します。Most of these cast operations involve some risk.

C スタイルのプログラミングでは、同じ C スタイルのキャスト演算子が、あらゆる種類のキャストに使用されます。In C-style programming, the same C-style cast operator is used for all kinds of casts.

(int) x; // old-style cast, old-style syntax
int(x); // old-style cast, functional syntax

つまり、C スタイルのキャスト演算子は、呼び出し演算子 () と同じものであり、したがって、コードの中で目立たず、簡単に見過ごす可能性があります。The C-style cast operator is identical to the call operator () and is therefore inconspicuous in code and easy to overlook. どちらも、ひとめで認識したり、検索したりするのは困難で、 static 、、およびの任意の組み合わせを呼び出すのに十分な違いがあるため、どちらも悪くありません const reinterpret_castBoth are bad because they're difficult to recognize at a glance or search for, and they're disparate enough to invoke any combination of static, const, and reinterpret_cast. 古いスタイルのキャストの実際の動作を理解するのは困難で、エラーが発生しやすくなります。Figuring out what an old-style cast actually does can be difficult and error-prone. これらすべての理由により、キャストがどうしても必要な場合は、次の C++ スタイルのキャスト操作のいずれかを使用することをお勧めします。特定の状況では、かなりタイプ セーフであり、プログラミングの意図を非常に明確に表現できるからです。For all these reasons, when a cast is required, we recommend that you use one of the following C++ cast operators, which in some cases are significantly more type-safe, and which express much more explicitly the programming intent:

  • static_cast。コンパイル時にのみチェックされるキャストの場合。static_cast, for casts that are checked at compile time only. static_cast 完全に互換性のない型間でキャストしようとしていることをコンパイラが検出すると、エラーが返されます。static_cast returns an error if the compiler detects that you are trying to cast between types that are completely incompatible. これを、基本型へのポインターと、派生形へのポインターの間でのキャストに使用することもできますが、コンパイラはこのような変換が実行時に安全かどうかを必ず判定できるわけではありません。You can also use it to cast between pointer-to-base and pointer-to-derived, but the compiler can't always tell whether such conversions will be safe at runtime.

    double d = 1.58947;
    int i = d;  // warning C4244 possible loss of data
    int j = static_cast<int>(d);       // No warning.
    string s = static_cast<string>(d); // Error C2440:cannot convert from
                                       // double to std:string
    
    // No error but not necessarily safe.
    Base* b = new Base();
    Derived* d2 = static_cast<Derived*>(b);
    

    詳細については、「static_cast」を参照してください。For more information, see static_cast.

  • dynamic_cast 安全なランタイムチェックのキャストの場合は、ポインターから派生クラスへのポインター。dynamic_cast, for safe, runtime-checked casts of pointer-to-base to pointer-to-derived. は、 dynamic_cast downcasts の場合よりも安全です static_cast が、ランタイムチェックでオーバーヘッドが発生します。A dynamic_cast is safer than a static_cast for downcasts, but the runtime check incurs some overhead.

    Base* b = new Base();
    
    // Run-time check to determine whether b is actually a Derived*
    Derived* d3 = dynamic_cast<Derived*>(b);
    
    // If b was originally a Derived*, then d3 is a valid pointer.
    if(d3)
    {
       // Safe to call Derived method.
       cout << d3->DoSomethingMore() << endl;
    }
    else
    {
       // Run-time check failed.
       cout << "d3 is null" << endl;
    }
    
    //Output: d3 is null;
    

    詳細については、「dynamic_cast」を参照してください。For more information, see dynamic_cast.

  • const_cast。変数の値をキャストする場合、 const または非変数をに変換する場合に使用 const const します。const_cast, for casting away the const-ness of a variable, or converting a non-const variable to be const. const この演算子を使用した非表示のキャストは、C スタイルのキャストを使用した場合と同様に、エラーが発生しやすくなります。ただし、を使用すると、 const_cast 誤ってキャストを実行する可能性は低くなります。Casting away const-ness by using this operator is just as error-prone as is using a C-style cast, except that with const_cast you are less likely to perform the cast accidentally. 場合によっては、変数を const 非パラメーターを受け取る関数に渡すために、変数の値をキャストする必要があり const const ます。Sometimes you have to cast away the const-ness of a variable, for example, to pass a const variable to a function that takes a non-const parameter. 次の例は、その方法を示したものです。The following example shows how to do this.

    void Func(double& d) { ... }
    void ConstCast()
    {
       const double pi = 3.14;
       Func(const_cast<double&>(pi)); //No error.
    }
    

    詳細については、「 const_cast」を参照してください。For more information, see const_cast.

  • reinterpret_cast ポインター型やなどの関連のない型間でキャストを行う場合は intreinterpret_cast, for casts between unrelated types such as a pointer type and an int.

    注意

    このキャスト演算子は、他のキャスト演算子ほどの頻度では使用されず、他のコンパイラへの移植性も保証されません。This cast operator is not used as often as the others, and it's not guaranteed to be portable to other compilers.

    次の例は、と reinterpret_cast の違いを示してい static_cast ます。The following example illustrates how reinterpret_cast differs from static_cast.

    const char* str = "hello";
    int i = static_cast<int>(str);//error C2440: 'static_cast' : cannot
                                  // convert from 'const char *' to 'int'
    int j = (int)str; // C-style cast. Did the programmer really intend
                      // to do this?
    int k = reinterpret_cast<int>(str);// Programming intent is clear.
                                       // However, it is not 64-bit safe.
    

    詳細については、「 reinterpret_cast Operator」を参照してください。For more information, see reinterpret_cast Operator.

関連項目See also

C++ 型システムC++ type system
C++ へようこそWelcome back to C++
C++ 言語リファレンスC++ Language Reference
C++ 標準ライブラリC++ Standard Library