ユーザー定義型の変換 (C++)User-Defined Type Conversions (C++)

A変換何らかの種類を異なる型の値からの新しい値を生成します。A conversion produces a new value of some type from a value of a different type. 標準変換、組み込み型できますを作成して、C++ 言語とサポートに組み込まれているユーザー定義の変換から、またはユーザー定義型の間の変換を実行します。Standard conversions are built into the C++ language and support its built-in types, and you can create user-defined conversions to perform conversions to, from, or between user-defined types.

標準変換は組み込み型の間、継承によって関連付けられる型へのポインターまたは参照の間、void ポインターを変換元または変換先として、および null ポインターを変換先として変換を実行します。The standard conversions perform conversions between built-in types, between pointers or references to types related by inheritance, to and from void pointers, and to the null pointer. 詳細については、次を参照してください。標準変換します。For more information, see Standard Conversions. ユーザー定義変換はユーザー定義型の間、またはユーザー定義型と組み込み型の間で変換を実行します。User-defined conversions perform conversions between user-defined types, or between user-defined types and built-in types. として実装することができます変換コンス トラクターまたは変換関数します。You can implement them as Conversion constructors or as Conversion functions.

変換は明示的 (キャストまたは直接の初期化などのように、ある型が別の型に変換されることをプログラマが要求するとき) または暗黙的 (プログラマによって指定された型と異なる型を言語またはプログラムが要求するとき) にすることができます。Conversions can either be explicit—when the programmer calls for one type to be converted to another, as in a cast or direct initialization—or implicit—when the language or program calls for a different type than the one given by the programmer.

暗黙的な変換は次の場合に試行されます。Implicit conversions are attempted when:

  • 関数に提供される引数が一致するパラメーターと同じ型を持たない。An argument supplied to a function does not have the same type as the matching parameter.

  • 関数から返される値が関数の戻り値の型と同じ型を持たない。The value returned from a function does not have the same type as the function return type.

  • 初期化子式が初期化されるオブジェクトと同じ型を持たない。An initializer expression does not have the same type as the object it is initializing.

  • 条件付きステートメント、ループ構造、またはスイッチを制御する式が制御に必要な結果の型を持たない。An expression that controls a conditional statement, looping construct, or switch does not have the result type that's required to control it.

  • 演算子に提供されるオペランドが一致するオペランド パラメーターと同じ型を持たない。An operand supplied to an operator does not have the same type as the matching operand-parameter. 組み込み演算子の場合、両方のオペランドは同じ型を持つ必要があり、両方を表す共通の型に変換されます。For built-in operators, both operands must have the same type, and are converted to a common type that can represent both. 詳細については、次を参照してください。標準変換します。For more information, see Standard Conversions. ユーザー定義演算子の場合、各オペランドは一致するオペランド パラメーターと同じ型を持つ必要があります。For user-defined operators, each operand must have the same type as the matching operand-parameter.

1 つの標準変換が暗黙的な変換を完了できない場合、コンパイラはユーザー定義変換を使用し、その後オプションとして追加の標準変換を使用して変換を完了します。When one standard conversion can't complete an implicit conversion, the compiler can use a user-defined conversion, followed optionally by an additional standard conversion, to complete it.

同じ変換を実行する 2 つ以上のユーザー定義変換が変換サイトで使用可能な場合、変換はあいまいであると言われます。When two or more user-defined conversions that perform the same conversion are available at a conversion site, the conversion is said to be ambiguous. コンパイラは使用可能な変換のどれを使用すべきか判別がつかないので、そうしたあいまいさはエラーになります。Such ambiguities are an error because the compiler can't determine which one of the available conversions it should choose. ただし、ソース コード内の異なる場所で使用可能な変換のセットは異なることがあるので (たとえば、ソース ファイルに含まれるヘッダー ファイルに依存)、同じ変換を実行する複数の方法を定義するだけではエラーになりません。However, it's not an error just to define multiple ways of performing the same conversion because the set of available conversions can be different at different locations in the source code—for example, depending on which header files are included in a source file. 変換サイトでただ 1 つの変換が使用可能である限り、あいまいさはありません。As long as only one conversion is available at the conversion site, there is no ambiguity. あいまいな変換が発生する原因にはいくつかの可能性がありますが、最も一般的な原因は次の通りです。There are several ways that ambiguous conversions can arise, but the most common ones are:

  • 多重継承。Multiple inheritance. 変換が複数の基底クラスで定義されています。The conversion is defined in more than one base class.

  • あいまいな関数呼び出し。Ambiguous function call. 変換がターゲット型の変換コンストラクターとして、かつ、ソース型の変換関数として定義されています。The conversion is defined as a conversion constructor of the target type and as a conversion function of the source type. 詳細については、次を参照してください。変換関数します。For more information, see Conversion functions.

通常、関係する型の名前を完全に修飾するか、または目的を明確化するために明示的なキャストを実行するだけであいまいさを解決できます。You can usually resolve an ambiguity just by qualifying the name of the involved type more fully or by performing an explicit cast to clarify your intent.

変換コンストラクターと変換関数は両方ともメンバー アクセス制御規則に従いますが、 変換のアクセシビリティは明確な変換が判別可能な場合にのみ考慮されます。Both conversion constructors and conversion functions obey member-access control rules, but the accessibility of the conversions is only considered if and when an unambiguous conversion can be determined. つまり、競合する変換のアクセス レベルによって使用できない可能性があっても、変換はあいまいになることがあります。This means that a conversion can be ambiguous even if the access level of a competing conversion would prevent it from being used. メンバーのアクセシビリティの詳細については、次を参照してください。メンバー アクセス コントロールします。For more information about member accessibility, see Member Access Control.

明示的なキーワードと暗黙的な変換の問題The explicit keyword and problems with implicit conversion

既定では、ユーザー定義変換を作成すると、コンパイラはユーザー定義変換を使用して暗黙的な変換を実行できます。By default when you create a user-defined conversion, the compiler can use it to perform implicit conversions. これが目的の動作である場合もありますが、コンパイラに暗黙的な変換を作成させる単純な規則によって、不要なコードが受け入れられてしまうことがあります。Sometimes this is what you want, but other times the simple rules that guide the compiler in making implicit conversions can lead it to accept code that you don't want it to.

問題が発生する暗黙的な変換の 1 つのよく知られている例への変換は、 boolします。One well-known example of an implicit conversion that can cause problems is the conversion to bool. ブール値のコンテキストで使用できるクラス型を作成する理由がたくさん — など、そのためことができますコントロールに、場合ステートメントまたはループ-コンパイラがへのユーザー定義の変換を実行しますが、組み込み型は、コンパイラは追加の標準変換後の適用を許可します。There are many reasons that you might want to create a class type that can be used in a Boolean context—for example, so that it can be used to control an if statement or loop—but when the compiler performs a user-defined conversion to a built-in type, the compiler is allowed to apply an additional standard conversion afterwards. この追加の標準変換の目的からの上位変換などを許可する短いintが明確でない変換のドアを開くことも — などからboolintクラス型想定しなかった整数のコンテキストで使用することができます。The intent of this additional standard conversion is to allow for things like promotion from short to int, but it also opens the door for less-obvious conversions—for example, from bool to int, which allows your class type to be used in integer contexts you never intended. この特定の問題と呼ばれる、安全なブール値問題します。This particular problem is known as the Safe Bool Problem. この種の問題は、where、明示的なキーワードが役立つことができます。This kind of problem is where the explicit keyword can help.

明示的なキーワードは、暗黙的な変換を実行する、指定された変換を使用できないことをコンパイラに指示します。The explicit keyword tells the compiler that the specified conversion can't be used to perform implicit conversions. 前に、暗黙的な変換の構文上便利な場合、明示的なキーワードが導入された、暗黙的な変換によって生じる予期しない結果を受け入れるか、あまり便利に使用する必要がありました回避策として変換関数の名前。If you wanted the syntactic convenience of implicit conversions before the explicit keyword was introduced, you had to either accept the unintended consequences that implicit conversion sometimes created or use less-convenient, named conversion functions as a workaround. 使用して、今すぐ、明示的なキーワード、明示的なキャストまたは直接の初期化を実行するのみ使用できる、安全なブール値問題典型的な例の問題の種類を生じると、便利な変換を作成することができます。Now, by using the explicit keyword, you can create convenient conversions that can only be used to perform explicit casts or direct initialization, and that won't lead to the kind of problems exemplified by the Safe Bool Problem.

明示的なキーワードは c++ 98 以降の変換コンス トラクターは c++ 11 以降の変換関数を適用できます。The explicit keyword can be applied to conversion constructors since C++98, and to conversion functions since C++11. 次のセクションでには、使用する方法の詳細が含まれて、明示的なキーワード。The following sections contain more information about how to use the explicit keyword.

変換コンス トラクターConversion constructors

変換コンストラクターはユーザー定義型または組み込み型からユーザー定義型への変換を定義します。Conversion constructors define conversions from user-defined or built-in types to a user-defined type. 次の例では、組み込み型から変換する変換コンス トラクター二重、ユーザー定義型にMoneyします。The following example demonstrates a conversion constructor that converts from the built-in type double to a user-defined type Money.

#include <iostream>

class Money
{
public:
    Money() : amount{ 0.0 } {};
    Money(double _amount) : amount{ _amount } {};

    double amount;
};

void display_balance(const Money balance)
{
    std::cout << "The balance is: " << balance.amount << std::endl;
}

int main(int argc, char* argv[])
{
    Money payable{ 79.99 };

    display_balance(payable);
    display_balance(49.95);
    display_balance(9.99f);

    return 0;
}

関数 display_balance の最初の呼び出しは Money 型の引数を取り、引数は正しい型なので変換は必要ないことに注意してください。Notice that the first call to the function display_balance, which takes an argument of type Money, doesn't require a conversion because its argument is the correct type. ただし、2 番目の呼び出しでdisplay_balance、ため、変換が必要、引数の型を二重の値を持つ49.95、いない関数で必要なものです。However, on the second call to display_balance, a conversion is needed because the type of the argument, a double with a value of 49.95, is not what the function expects. 関数が直接、この値を使用することはできません引数の型からの変換があるためですが、—二重-一致するパラメーターの型に —Money-型の一時的な値Moneyから構築されます。引数は、関数呼び出しを完了するために使用します。The function can't use this value directly, but because there's a conversion from the type of the argument—double—to the type of the matching parameter—Money—a temporary value of type Money is constructed from the argument and used to complete the function call. 3 番目の呼び出しでdisplay_balance、引数ではありませんが、二重が代わりには、 floatの値を持つ9.99-まだ関数呼び出しは完了できますので、コンパイラは標準変換を実行できます: からこの例では、 float二重— からユーザー定義の変換を実行し、二重Moneyために必要な変換を完了します。In the third call to display_balance, notice that the argument is not a double, but is instead a float with a value of 9.99—and yet the function call can still be completed because the compiler can perform a standard conversion—in this case, from float to double—and then perform the user-defined conversion from double to Money to complete the necessary conversion.

変換コンストラクターの宣言Declaring conversion constructors

次の規則は変換コンストラクターの宣言に適用されます。The following rules apply to declaring a conversion constructor:

  • 変換のターゲット型は構築されるユーザー定義型です。The target type of the conversion is the user-defined type that's being constructed.

  • 通常、変換コンストラクターは、ソース型を持つ、ただ 1 つの引数を取ります。Conversion constructors typically take exactly one argument, which is of the source type. ただし、変換コンストラクターは追加パラメーターを指定できます (各追加パラメーターが既定値を持つ場合)。However, a conversion constructor can specify additional parameters if each additional parameter has a default value. ソース型は最初のパラメーターの型を維持します。The source type remains the type of the first parameter.

  • 変換コンストラクターはすべてのコンストラクターと同様に戻り値の型を指定しません。Conversion constructors, like all constructors, do not specify a return type. 宣言内で戻り値の型を指定するとエラーになります。Specifying a return type in the declaration is an error.

  • 変換コンストラクターは明示的にすることができます。Conversion constructors can be explicit.

明示的な変換コンストラクターExplicit conversion constructors

変換コンス トラクターを宣言することで明示的なでのみ使用できますか、明示的なキャストを実行するオブジェクトの直接の初期化を実行します。By declaring a conversion constructor to be explicit, it can only be used to perform direct initialization of an object or to perform an explicit cast. これにより、このクラス型の引数を受け入れる関数が変換コンストラクターのソース型の引数も暗黙的に受け入れるのを防ぎ、クラス型がソース型の値からコピー初期化されるのを防ぎます。This prevents functions that accept an argument of the class type from also implicitly accepting arguments of the conversion constructor's source type, and prevents the class type from being copy-initialized from a value of the source type. 次の例は、明示的な変換コンストラクターの定義方法とコードの整形における効果を示します。The following example demonstrates how to define an explicit conversion constructor, and the effect it has on what code is well-formed.

#include <iostream>

class Money
{
public:
    Money() : amount{ 0.0 } {};
    explicit Money(double _amount) : amount{ _amount } {};

    double amount;
};

void display_balance(const Money balance)
{
    std::cout << "The balance is: " << balance.amount << std::endl;
}

int main(int argc, char* argv[])
{
    Money payable{ 79.99 };        // Legal: direct initialization is explicit.

    display_balance(payable);      // Legal: no conversion required
    display_balance(49.95);        // Error: no suitable conversion exists to convert from double to Money.
    display_balance((Money)9.99f); // Legal: explicit cast to Money

    return 0;
}

この例で、明示的な変換コンストラクターを使用して、payable の直接の初期化を実行できることに注意してください。In this example, notice that you can still use the explicit conversion constructor to perform direct initialization of payable. 代わりに Money payable = 79.99; をコピー初期化しようとするとエラーになります。If instead you were to copy-initialize Money payable = 79.99;, it would be an error. display_balance の最初の呼び出しは引数が正しい型なので影響を受けません。The first call to display_balance is unaffected because the argument is the correct type. display_balance の 2 番目の呼び出しは、変換コンストラクターが暗黙的な変換を実行するために使用できないため、エラーになります。The second call to display_balance is an error, because the conversion constructor can't be used to perform implicit conversions. 3 番目の呼び出しdisplay_balance明示的にキャストするために有効ですMoney、コンパイラがまだ支援通知からの暗黙的なキャストを挿入することで、キャストの完了がfloat.The third call to display_balance is legal because of the explicit cast to Money, but notice that the compiler still helped complete the cast by inserting an implicit cast from float to double.

暗黙的な変換を許すことによる利便性は魅力的ですが、そうすると発見困難なバグを誘発する恐れがあります。Although the convenience of allowing implicit conversions can be tempting, doing so can introduce hard-to-find bugs. 経験則では、特定の変換を暗黙的に発生させたいことが確かである場合を除き、すべての変換コンストラクターを明示的にすることです。The rule of thumb is to make all conversion constructors explicit except when you're sure that you want a specific conversion to occur implicitly.

変換関数Conversion functions

変換関数はユーザー定義型から他の型への変換を定義します。Conversion functions define conversions from a user-defined type to other types. これらの関数は、値が異なる型にキャストされるときに、変換コンストラクターと共に呼び出されるため、「キャスト演算子」と呼ばれることがあります。These functions are sometimes referred to as "cast operators" because they, along with conversion constructors, are called when a value is cast to a different type. 次の例では、ユーザー定義の型から変換する変換関数Money、組み込みの型に二重:The following example demonstrates a conversion function that converts from the user-defined type, Money, to a built-in type, double:

#include <iostream>

class Money
{
public:
    Money() : amount{ 0.0 } {};
    Money(double _amount) : amount{ _amount } {};

    operator double() const { return amount; }
private:
    double amount;
};

void display_balance(const Money balance)
{
    std::cout << "The balance is: " << balance << std::endl;
}

注意メンバー変数amountプライベートおよびパブリック変換関数を入力することに加え二重はの値を返すだけに導入されましたamountNotice that the member variable amount is made private and that a public conversion function to type double is introduced just to return the value of amount. 関数 display_balance では、ストリーム挿入演算子 balance を使用することにより、<< の値が標準出力にストリームされるときに暗黙的な変換が発生します。In the function display_balance, an implicit conversion occurs when the value of balance is streamed to standard output by using the stream insertion operator <<. ユーザー定義型のストリーム挿入演算子が定義されていないためMoney、組み込み型の 1 つであるが、二重、コンパイラからの変換関数が使用できるMoneyストリーム挿入演算子を満たすためにします。Because no stream-insertion operator is defined for the user-defined type Money, but there is one for built-in type double, the compiler can use the conversion function from Money to double to satisfy the stream-insertion operator.

変換関数は派生クラスによって継承されます。Conversion functions are inherited by derived classes. 派生クラスの変換関数は、完全に同じ型に変換する場合にのみ、継承された変換関数をオーバーライドします。Conversion functions in a derived class only override an inherited conversion function when they convert to exactly the same type. たとえば、ユーザー定義の変換関数、派生クラスの演算子 intを上書きしません- または偶数の影響: 基底クラスのユーザー定義の変換関数演算子 shortも、間の変換関係を定義する標準変換がint短いします。For example, a user-defined conversion function of the derived class operator int does not override—or even influence—a user-defined conversion function of the base class operator short, even though the standard conversions define a conversion relationship between int and short.

変換関数の宣言Declaring conversion functions

次の規則は変換関数の宣言に適用されます。The following rules apply to declaring a conversion function:

  • 変換のターゲット型は変換関数の宣言の前に宣言する必要があります。The target type of the conversion must be declared prior to the declaration of the conversion function. クラス、構造体、列挙型、typedef は変換関数の宣言内で宣言できません。Classes, structures, enumerations, and typedefs cannot be declared within the declaration of the conversion function.

    operator struct String { char string_storage; }() // illegal
    
  • 変換関数は引数を受け取りません。Conversion functions take no arguments. 宣言内でパラメーターを指定するとエラーになります。Specifying any parameters in the declaration is an error.

  • 変換関数は、変換関数の名前 (変換のターゲット型の名前でもあります) で指定される戻り値の型を持ちます。Conversion functions have a return type that is specified by the name of the conversion function, which is also the name of the conversion's target type. 宣言内で戻り値の型を指定するとエラーになります。Specifying a return type in the declaration is an error.

  • 変換関数は仮想にすることができます。Conversion functions can be virtual.

  • 変換関数は明示的にすることができます。Conversion functions can be explicit.

明示的な変換関数Explicit conversion functions

変換関数を明示的に宣言すると、明示的なキャストを実行するためだけに使用できます。When a conversion function is declared to be explicit, it can only be used to perform an explicit cast. これにより、変換関数のターゲット型の引数を受け入れる関数がクラス型の引数も暗黙的に受け入れるのを防ぎ、ターゲット型のインスタンスがクラス型の値からコピー初期化されるのを防ぎます。This prevents functions that accept an argument of the conversion function's target type from also implicitly accepting arguments of the class type, and prevents instances of the target type from being copy-initialized from a value of the class type. 次の例は、明示的な変換関数の定義方法とコードの整形における効果を示します。The following example demonstrates how to define an explicit conversion function and the effect it has on what code is well-formed.

#include <iostream>

class Money
{
public:
    Money() : amount{ 0.0 } {};
    Money(double _amount) : amount{ _amount } {};

    explicit operator double() const { return amount; }
private:
    double amount;
};

void display_balance(const Money balance)
{
    std::cout << "The balance is: " << (double)balance << std::endl;
}

ここでは、変換関数演算子 doubleは、明示的な型に明示的なキャスト二重が関数に導入されましたdisplay_balance変換を実行します。Here the conversion function operator double has been made explicit, and an explicit cast to type double has been introduced in the function display_balance to perform the conversion. このキャストを省略すると、コンパイラは << 型に対する適切なストリーム挿入演算子 Money を特定できず、エラーが発生します。If this cast were omitted, the compiler would be unable to locate a suitable stream-insertion operator << for type Money and an error would occur.