C++ 타입 변환 | "Type Conversion" 가이드
이 글의 핵심
C++ 타입 변환에 대해 정리한 개발 블로그 글입니다. int x = 10; double y = x;
암시적 변환
컴파일러가 자동으로 수행하는 변환
int x = 10;
double y = x; // int -> double (암시적)
float f = 3.14;
int z = f; // float -> int (암시적, 소수점 버림)
bool b = 42; // int -> bool (0이 아니면 true)
명시적 변환 (캐스팅)
double d = 3.14;
// C 스타일 캐스트
int x = (int)d;
// C++ 스타일 캐스트
int y = static_cast<int>(d);
변환 생성자
class Distance {
private:
double meters;
public:
// 변환 생성자
Distance(double m) : meters(m) {}
double getMeters() const {
return meters;
}
};
void printDistance(Distance d) {
cout << d.getMeters() << "m" << endl;
}
int main() {
printDistance(100.5); // double -> Distance (암시적)
printDistance(Distance(50.0)); // 명시적
}
explicit 키워드
class Distance {
private:
double meters;
public:
// explicit: 암시적 변환 금지
explicit Distance(double m) : meters(m) {}
double getMeters() const {
return meters;
}
};
void printDistance(Distance d) {
cout << d.getMeters() << "m" << endl;
}
int main() {
// printDistance(100.5); // 에러: 암시적 변환 불가
printDistance(Distance(100.5)); // OK: 명시적
}
변환 연산자
class Distance {
private:
double meters;
public:
Distance(double m) : meters(m) {}
// 변환 연산자: Distance -> double
operator double() const {
return meters;
}
};
int main() {
Distance d(100.5);
double x = d; // Distance -> double (암시적)
cout << x << endl; // 100.5
}
실전 예시
예시 1: 문자열 래퍼
class String {
private:
string data;
public:
String(const char* str) : data(str) {}
explicit String(const string& str) : data(str) {}
// string으로 변환
operator string() const {
return data;
}
// const char*로 변환
operator const char*() const {
return data.c_str();
}
size_t length() const {
return data.length();
}
};
int main() {
String s("Hello");
string str = s; // String -> string
const char* cstr = s; // String -> const char*
cout << str << endl;
cout << cstr << endl;
}
예시 2: 스마트 포인터
template<typename T>
class SmartPtr {
private:
T* ptr;
public:
explicit SmartPtr(T* p = nullptr) : ptr(p) {}
~SmartPtr() {
delete ptr;
}
// bool로 변환 (nullptr 체크)
explicit operator bool() const {
return ptr != nullptr;
}
T& operator*() const {
return *ptr;
}
T* operator->() const {
return ptr;
}
};
int main() {
SmartPtr<int> p1(new int(42));
SmartPtr<int> p2;
if (p1) { // bool로 변환
cout << *p1 << endl;
}
if (!p2) {
cout << "p2는 nullptr" << endl;
}
}
예시 3: 온도 클래스
class Celsius {
private:
double temp;
public:
explicit Celsius(double t) : temp(t) {}
double get() const {
return temp;
}
};
class Fahrenheit {
private:
double temp;
public:
explicit Fahrenheit(double t) : temp(t) {}
// Celsius로 변환
Fahrenheit(const Celsius& c)
: temp(c.get() * 9.0 / 5.0 + 32.0) {}
double get() const {
return temp;
}
};
int main() {
Celsius c(100.0);
Fahrenheit f = Fahrenheit(c); // Celsius -> Fahrenheit
cout << "섭씨 " << c.get() << "도" << endl;
cout << "화씨 " << f.get() << "도" << endl;
}
예시 4: 분수 클래스
class Fraction {
private:
int numerator;
int denominator;
public:
Fraction(int n, int d = 1)
: numerator(n), denominator(d) {}
// double로 변환
explicit operator double() const {
return static_cast<double>(numerator) / denominator;
}
// int로 변환 (정수 부분만)
explicit operator int() const {
return numerator / denominator;
}
void print() const {
cout << numerator << "/" << denominator;
}
};
int main() {
Fraction f(3, 4);
double d = static_cast<double>(f); // 0.75
int i = static_cast<int>(f); // 0
cout << "분수: ";
f.print();
cout << endl;
cout << "실수: " << d << endl;
cout << "정수: " << i << endl;
}
표준 변환 순서
// 1. 정수 승격
char c = 'A';
int x = c; // char -> int
// 2. 정수 변환
long l = 100;
int y = l; // long -> int
// 3. 부동소수점 변환
float f = 3.14f;
double d = f; // float -> double
// 4. 포인터 변환
int* p = nullptr; // nullptr -> int*
// 5. bool 변환
bool b = 42; // int -> bool
사용자 정의 변환 규칙
class A {
public:
A(int x) {} // int -> A
};
class B {
public:
B(const A& a) {} // A -> B
};
int main() {
// int -> A -> B (최대 1번의 사용자 정의 변환)
// B b = 42; // 에러: 2번의 변환 필요
A a = 42; // OK: int -> A
B b = a; // OK: A -> B
}
자주 발생하는 문제
문제 1: 의도하지 않은 변환
class Array {
private:
int* data;
size_t size;
public:
// ❌ 암시적 변환 허용
Array(size_t s) : size(s), data(new int[s]) {}
~Array() {
delete[] data;
}
};
void func(Array arr) {}
int main() {
func(10); // 의도하지 않은 변환
}
// ✅ explicit 사용
class Array {
public:
explicit Array(size_t s) : size(s), data(new int[s]) {}
// ...
};
// func(10); // 에러
func(Array(10)); // OK
문제 2: 변환 연산자 남용
class Number {
private:
int value;
public:
Number(int v) : value(v) {}
// ❌ 암시적 변환 (혼란 야기)
operator int() const {
return value;
}
};
Number n(42);
int x = n + 10; // 혼란스러움
// ✅ explicit 사용
class Number {
public:
explicit operator int() const {
return value;
}
};
int x = static_cast<int>(n) + 10; // 명확
문제 3: 변환 체인
class A {
public:
A(int x) {}
};
class B {
public:
B(const A& a) {}
};
// ❌ 2번의 변환 (불가)
// B b = 42; // int -> A -> B
// ✅ 명시적 변환
A a = 42;
B b = a;
// 또는
B b = B(A(42));
explicit(bool) (C++20)
template<typename T>
class Optional {
private:
T value;
bool hasValue;
public:
Optional() : hasValue(false) {}
Optional(const T& v) : value(v), hasValue(true) {}
// 조건부 explicit
template<typename U>
explicit(!is_convertible_v<U, T>)
Optional(const U& v) : value(v), hasValue(true) {}
explicit operator bool() const {
return hasValue;
}
};
FAQ
Q1: explicit은 언제 사용?
A:
- 단일 인자 생성자
- 변환 연산자
- 의도하지 않은 변환 방지
Q2: 변환 생성자 vs 변환 연산자?
A:
- 변환 생성자: 다른 타입 → 내 타입
- 변환 연산자: 내 타입 → 다른 타입
Q3: C 스타일 vs C++ 스타일 캐스트?
A: C++ 스타일 권장 (명확, 안전).
Q4: 암시적 변환은 나쁜가?
A: 편리하지만 버그 유발 가능. explicit으로 제어.
Q5: 변환 체인은?
A: 최대 1번의 사용자 정의 변환만 허용.
Q6: 타입 변환 학습 리소스는?
A:
- “Effective C++”
- cppreference.com
- “C++ Primer”
같이 보면 좋은 글 (내부 링크)
이 주제와 연결되는 다른 글입니다.
- C++ explicit Keyword | “explicit 키워드” 가이드
- C++ explicit Keyword | “explicit Keyword” Guide
- C++ 캐스팅 | “static_cast/dynamic_cast” 4가지 완벽 정리
관련 글
- C++ Copy Initialization |