On this page
article
C++ Type Conversion
타입 변환(Conversion)
C++에서는 객체와 다른 타입 간의 변환을 위한 다양한 방법을 제공합니다.
변환 연산자
객체가 다른 타입으로 변환될 때 호출되는 함수:
class Int32 {
int value;
public:
Int32() : value(0) {}
Int32(int n) : value(n) {}
// Int32를 int로 변환하는 연산자
operator int() const { return value; }
};
int main() {
int pn;
Int32 un;
pn = un; // un.operator int() 호출
un = pn; // 아래 두 가지 중 하나로 해석
// 1. un.operator=(pn) - 대입 연산자
// 2. un = Int32(pn) - 변환 생성자 + 대입
}
타입 변환 초기화 방법
Int32 n1(3); // 직접 초기화
Int32 n2 = 3; // 복사 초기화 -> Int32 n2 = Int32(3)와 동일
Int32 n3{3}; // 유니폼 초기화(C++11)
Int32 n4 = {3}; // 유니폼 초기화 + 복사 초기화
n1 = 3; // 대입 연산에서의 변환
C++14까지의 처리 과정 (Int32 n2 = 3)
- 인자 1개인 생성자를 이용해 Int32 임시 객체 생성
- 생성된 임시 객체를 복사/move 생성자로 복사
- 컴파일러가 최적화하여 임시 객체 생성을 제거하는 경우가 많음
C++17 이후
임시 객체를 생성하지 않고, 인자 1개인 생성자를 직접 호출하는 방식으로 최적화됨
explicit 키워드
생성자나 변환 연산자가 암시적 변환의 용도로 사용되는 것을 방지합니다.
explicit 생성자
class Vector {
public:
explicit Vector(int size) {}
};
void foo(Vector v) {}
int main() {
Vector v1(3); // 직접 초기화 - 허용
// Vector v2 = 3; // 복사 초기화 - 오류
// v1 = 3; // 대입 - 오류
// foo(3); // 함수 인자로 암시적 변환 - 오류
}
explicit 변환 연산자
class Machine {
bool state;
public:
// explicit가 없으면 다른 변환 연산자와 충돌 가능
explicit operator bool() {
return state;
}
};
Machine m;
if (m) { /* m이 유효한지 검사 */ }
// explicit가 없다면 아래와 같은 잘못된 코드도 컴파일됨
// m << 10; // bool로 변환 후 비트 시프트 연산 시도
C++20: 조건부 explicit
C++20부터는 조건에 따라 explicit 동작을 설정할 수 있습니다:
template<typename T>
class Wrapper {
public:
// T가 정수 타입이 아닐 때만 explicit
explicit(!std::is_integral_v<T>) Wrapper(T value);
};
특수 변환 예제
nullptr
struct nullptr_t {
template<typename T>
constexpr operator T*() const {
return 0;
}
};
nullptr_t null_ptr;
void foo(int* p) {}
int main() {
foo(null_ptr); // operator int*() 호출
}
반환 타입 추론(Return Type Resolver)
struct Alloc {
std::size_t size;
Alloc(std::size_t sz) : size(sz) {}
template<typename T>
operator T*() {
return new T[size];
}
};
int main() {
int* p1 = Alloc(10); // operator int*() 호출
double* p2 = Alloc(10); // operator double*() 호출
}
이 예제에서 Alloc
클래스는 템플릿 변환 연산자를 통해 어떤 포인터 타입으로도 변환될 수 있습니다. 변환 과정에서 요청된 타입의 배열을 동적으로 할당하여 반환합니다.
int* p1 = Alloc(10);
에서는Alloc(10)
객체가operator int*()
변환을 통해int[10]
배열을 할당하고 그 포인터를 반환합니다.double* p2 = Alloc(10);
에서는 같은Alloc(10)
객체가operator double*()
변환을 통해double[10]
배열을 할당하고 그 포인터를 반환합니다.
이런 패턴은 사용 컨텍스트에 따라 자동으로 적절한 타입의 메모리를 할당하는 스마트 할당자를 구현할 때 유용합니다. 단, 메모리 누수를 방지하기 위해 실제 사용 시에는 적절한 메모리 관리 전략(스마트 포인터 등)이 필요합니다.