사용자 정의 형식 변환(C++)User-Defined Type Conversions (C++)

변환은 다른 형식의 값에서 형식의 새 값을 생성 합니다.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.

한 개의 표준 변환이 암시적 변환을 완료할 수 없는 경우 컴파일러는 사용자 정의 변환을 사용하여(필요에 따라 표준 변환을 추가적으로 수행하여) 암시적 변환을 완료할 수 있습니다.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.

변환 사이트에 동일한 변환을 수행하는 사용자 정의 변환이 둘 이상 있는 경우에는 변환이 모호합니다.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. 변환 사이트에 사용 가능한 변환이 하나만 있다면 모호성이 없습니다.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. 멤버 접근성에 대 한 자세한 내용은 member Access Control를 참조 하세요.For more information about member accessibility, see Member Access Control.

explicit 키워드 및 암시적 변환 문제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.

문제를 일으킬 수 있는 암시적 변환의 잘 알려진 예 중 하나는로 변환 하는 것입니다 bool .One well-known example of an implicit conversion that can cause problems is the conversion to bool. 부울 컨텍스트에서 사용할 수 있는 클래스 형식 (예: 문 또는 루프를 제어 하는 데 사용할 수 있음)을 만들 수 있는 여러 가지 이유가 if 있지만, 컴파일러가 기본 제공 형식으로 사용자 정의 변환을 수행 하는 경우 컴파일러는 나중에 추가 표준 변환을 적용할 수 있습니다.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. 이러한 추가 표준 변환의 목적은에서로의 프로 모션과 같은 작업을 허용 하는 것 short int 이지만, 보다 명확 하지 않은 변환에 대 한 도어 (예:에서로)를 사용 하 여 bool int 클래스 형식을 사용자가 의도 하지 않은 정수 컨텍스트에서 사용할 수 있습니다.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. 이 특정 문제를 안전한 Bool 문제 라고 합니다.This particular problem is known as the Safe Bool Problem. 이러한 종류의 문제는 키워드에서 explicit 도움이 될 수 있습니다.This kind of problem is where the explicit keyword can help.

explicit 키워드는 지정 된 변환을 사용 하 여 암시적 변환을 수행할 수 없음을 컴파일러에 알립니다.The explicit keyword tells the compiler that the specified conversion can't be used to perform implicit conversions. 키워드를 도입 하기 전에 암시적 변환의 구문을 편리 하 게 사용 하려는 경우 암시적 변환으로 인해 발생 explicit 하는 의도 하지 않은 결과를 그대로 사용 하거나, 보다 편리 하 고 명명 된 변환 함수를 해결 방법으로 사용 해야 했습니다.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. 이제 키워드를 사용 하 여 explicit 명시적 캐스트 또는 직접 초기화를 수행 하는 데만 사용할 수 있는 편리한 변환을 만들 수 있으며,이로 인해 Safe Bool 문제로 인해 발생 하는 문제 종류는 예시 않습니다.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.

explicit 키워드는 c + + 98 이후의 변환 생성자와 c + + 11 이후의 변환 함수에 적용 될 수 있습니다.The explicit keyword can be applied to conversion constructors since C++98, and to conversion functions since C++11. 다음 섹션에는 키워드를 사용 하는 방법에 대 한 자세한 내용이 포함 되어 explicit 있습니다.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. 다음 예제에서는 기본 제공 형식에서 double 사용자 정의 형식으로 변환 하는 변환 생성자를 보여 줍니다 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. 그러나에 대 한 두 번째 호출에서 display_balance 인수의 형식 ( double 값이 인)이 함수에 필요한 항목이 아니기 때문에 변환이 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. 함수는이 값을 직접 사용할 수 없지만 인수 형식에서 double 일치 하는 매개 변수의 형식으로 변환 하기 때문에 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. 에 대 한 세 번째 호출에서 인수는이 display_balance 아니라 값이 double 인 인입니다. 그러나 float 9.99 컴파일러가 표준 변환 (이 경우에서 float 로)을 수행한 double 다음에서로의 사용자 정의 변환을 수행 double 하 여 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.

  • 일반적으로 변환 생성자에는 소스 형식에 대한 단 하나의 인수만 사용됩니다.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

변환 생성자를로 선언 하 여 explicit 개체의 직접 초기화를 수행 하거나 명시적 캐스트를 수행 하는 데만 사용할 수 있습니다.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에 대한 두 번째 호출의 경우에는 암시적 변환을 수행하는 데 변환 생성자를 사용할 수 없으므로 오류입니다.The second call to display_balance is an error, because the conversion constructor can't be used to perform implicit conversions. 에 대 한 명시적 캐스트로 인해에 대 한 세 번째 호출은 display_balance 유효 Money 하지만, 컴파일러는에서로의 암시적 캐스트를 삽입 하 여 캐스트를 완료 하는 데 도움이 됩니다 float double .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 을 기본 제공 형식으로 변환 하는 변환 함수를 보여 줍니다 double .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;
}

멤버 변수는 private으로 설정 되 amount 고 형식에 대 한 공용 변환 함수는 double 의 값을 반환 하기 위해 도입 되었습니다 amount .Notice 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 double 컴파일러는의 변환 함수를 사용 하 Moneydouble 스트림 삽입 연산자를 만족할 수 있습니다.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 의 사용자 정의 변환 함수는 표준 변환이 및 간의 변환 관계를 정의 하는 경우에도 기본 클래스 operator short 의 사용자 정의 변환 함수를 재정의 하거나 영향을 주지 않습니다 int short .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 이 명시적으로 설정 되 고, 형식에 대 한 명시적 캐스트가 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.