Programming Language/C++

[C++] Implicit Conversion(묵시적 형변환)

lumana 2024. 6. 27. 17:31

Implicit Conversion(묵시적 형변환)


묵시적 형변환과 명시적 형변환

  • 묵시적(암시적) 형변환: implicit conversion

    • 형변환을 겉으로 드러내지 않음
  • 명시적 형변환: explicit conversion

    • 형변환을 명시한다

변환 생성자

#include <iostream>
#include <string>
using namespace std;

class Item {
   public:
    Item(int num) : num(num) {  // 변환 생성자
        cout << "Item(int)" << '\n';
    }
    Item(string name) : name(name) {  // 변환 생성자
        cout << "Item(string)" << '\n';
    }
    Item(int num, string name) : num(num), name(name) {
        cout << "Item(int, string)" << '\n';
    }
    void print() {
        cout << num << " : " << name << endl;
    }

   private:
    int num;
    string name;
};

int main(void) {
    Item i1 = Item(1);
    Item i2(2);
    Item i3 = 3; // 변환 생성자(int num)
    i3 = 3;
    // 3을 item객체로 변환시킴(임시객체 생성) --> 이동생성자를 통해 i3로 대입
    // 묵시적 변환

    Item i4 = (Item)4;  // Item(int num) 생성자가 형 변환까지 하고 있음.
    // 명시적 변환
  • 전부 Item(int num) 생성자를 호출

    • 우리가 알고 있는 일반적인 일반적인 생성자의 역할도 하지만, 변환 생성자의 역할도 한다.
  • Item i3 = 3;

    • 변환 생성자에서 임시 객체를 생성한 후 임시 객체를 통해서 i3를 초기화
  • i3 = 3

    • 컴파일러는 정수 3이 i3로 변환될 수 있는지를 찾는다.

    • 변환 생성자가 존재 -> 변환 생성자 Item(int)가 호출되어 임시 객체가 생성된다

    • 기본 대입 연산자를 통해 i3에 대입된다

  • 아래 코드도 변환 생성자를 통해 대입된다

Item i5(5);
i5 = string("stone");
  • 그러면 파라미터가 2개인 생성자를 호출하면?
Item i6(1, "grass");
Item i7 = { 2, "dirt" };
// 다음도 가능
i7 = { 2, "dirt" }; // C++11 문법, 묵시적 변환
Item i8{ 3, "wood" };
  • 매개변수가 2개인 경우에 대해서도 위에서 본 것과 같이 변환 생성자를 통해서 작동한다

  • 실제로 테스트를 해보자

i1.print();
i2.print();
i3.print();
i4.print();
i5.print();
i6.print();
i7.print();
i8.print();

형변환 연산자 오버로딩

  • 다음은 불가능하다
int itemNum = (int)i8;
  • 형변환도 연산자이기 때문에, 연산자 오버로딩을 해줄 수 있다.
class Item {
        // Item -> int 형변환 연산자 오버로딩
    operator int() const { // explicit operator int() : 명시적으로만 가능하게
        return num;
    }
}
  • 반환 타입을 operator 키워드 뒤에 적어줌

    • 연산자에서 반환 타입을 알 수 있기 때문에 operator 앞에 int 등의 타입을 안 적는 것

    • 객체 자체가 안바뀌도록 const를 붙여주는게 일반적이다

  • 이렇게 하면 위에서 불가능 했던 것이 가능해짐

int itemNum1 = (int)i8;
// 묵시적도 가능하다
int itemNum2 = i7;
  • String 타입으로도 바꿔보자
// Item -> string
operator string() const {
        return to_string(num) + " : " + name;
        // 참고) to_string : 숫자를 문자열로 반환하여 return
}

void println(string s) {
    cout << s << endl;
}
  • 출력을 해보자
int itemNum = (int)i8;  // 명시적
int itemNum2 = i7;      // 묵시적
cout << itemNum << endl;
cout << itemNum2 << endl;

println((string)i8);  // 명시적 형변환
println((int)i8);
  • 만약 다음과 같이 출력한다면?
println(i8);
  • string으로 묵시적 형변환이 일어난다

  • 만약 다음과 같은 함수가 추가로 존재한다면?

void println(int n) {
    cout << n << endl;
}
  • println(i8)에서 오류가 발생한다.

    • 묵시적 형변환시 i8을 string으로? Int로? 어떤 걸로 변환해야 하는지 모호성 발생

explicit 키워드

  • 형변환 연산자 앞에 explicit 키워드를 붙여주면 묵시적으로 해당 형변환 연산자가 실행되지 않도록 만든다

  • 주로 원하지 않은 다른 타입으로 묵시적 변환이 발생하는 것을 방지하기 위해 사용한다

explicit operator string() const {
    return to_string(num) + " : " + name;
}
  • 변환 생성자에 explicit을 붙여주면 묵시적으로 Item형 임시 객체를 만드는 것이 불가능해져서 에러가 발생한다
class Item {
   public:
    explicit Item(int num) : num(num) {  // 변환 생성자 // explicit Item(int num) : 명시적으로만 가능하게
        cout << "Item(int)" << '\n';
    }
    explicit Item(string name) : name(name) {  // 변환 생성자
        cout << "Item(string)" << '\n';
    }
    explicit Item(int num, string name) : num(num), name(name) {
        cout << "Item(int, string)" << '\n';
    }
        /* 이하 생략 */
}

int main(void) {
    Item i1 = Item(1);
    Item i2(2);
    Item i3 = 3; // 에러 발생 : 묵시적 형변환 불가
    i3 = 3; // 에러 발생 : 묵시적 형변환 불가