안녕하세요.
C++에서 if(expression) 문은 가장 기본적인 제어문 하나입니다.
우리는 보통 표현식(expression) 자리에 true 또는 false를 반환하는 비교 연산(i > j 등)이 알고 있습니다.
하지만 C++의 if문은 생각보다 훨씬 유연하게 동작합니다. 그리고 바로 그 부분이 미묘한 버그의 원인이 되기도 합니다.
이번 글에서는 if문의 조건식이 어떻게 평가되는지, 그리고 C++11의 explicit 키워드가 사용하기 전에 개발자들이 bool 타입에 대한 내용에 대해 재미 삼아 알아보겠습니다.
1. 숫자와 포인터도 받아주는 if문
C++의 if문은 조건식의 결과가 꼭 bool 타입일 필요가 없습니다. 0이 아닌 숫자나 null이 아닌 포인터 등 참(true)으로 평가될 수 있는 모든 값이 조건 자리에 올 수 있습니다.
1) 숫자를 조건으로 사용하기
아래 코드에서 i < j라는 조건이 참(true)이므로 printf 문을 실행합니다. 재밌는 점은 i < j의 실제 결괏값은 bool 타입의 true가 아니라, 정수 1이라는 점입니다.
C++에서는 0을 false로, 그 외 모든 숫자를 true로 간주하기 때문에 정상적으로 동작합니다.
#include <stdio.h>
int main() {
int i = 1;
int j = 7;
if (i < j) {
printf("i < j의 결과: %d\n", i < j);
printf("print\n");
}
return 0;
}
2) 포인터를 조건으로 사용하기
문자열 리터럴(`"address"`)도 `if`문의 조건이 될 수 있습니다.
이 경우, 문자열의 시작 주소값(포인터)이 반환되는데, 이 주소값은 nullptr가 아니므로 true로 평가되어 if문 내부의 코드가 실행됩니다.
#include <stdio.h>
int main() {
if ("address") {
printf("if print\n");
}
return 0;
}
2. 클래스와 연동 시 암시적 형 변환 문제요인
만약 우리가 직접 만든 클래스 객체를 if문의 조건으로 사용한다면 어떻게 될까요? 이때 형 변환 연산자(conversion operator)를 사용합니다.
아래 IF_TEST 클래스는 bool, int, int* 세 가지 타입으로 변환될 수 있는 방법을 모두 정의했습니다.
#include <stdio.h>
class IF_TEST
{
private:
static const int isStatic = 0;
public:
operator bool() const {
printf("operator bool() 호출됨\n");
return true;
}
operator int() const {
printf("operator int() 호출됨\n");
return 2;
}
operator int* () const {
printf("operator int*() 호출됨\n");
return const_cast<int*>(&isStatic);
}
};
int main() {
IF_TEST t;
if (t) { // 이 시점에 어떤 형 변환 연산자가 호출될까요?
printf("if문 실행\n");
}
return 0;
}
if(t)가 실행될 때, 컴파일러는 t 객체를 bool 타입으로 변환하려고 시도합니다. IF_TEST 클래스에는 bool로 변환할 수 있는 operator bool()가 명확하게 정의되어 있으므로, 다른 변환 연산자(operator int, operator int*)보다 우선적으로 선택됩니다.
즉, bool로의 변환이 가장 가까운 변환이기 때문입니다.
만약 operator bool()가 없다면(주석 처리한다면), 컴파일러는 operator int()나 operator int*()를 이용해 bool로 변환을 시도할 수 있습니다.
하지만 두 가지 이상의 변환 경로가 존재하면 어떤 것을 선택해야 할지 모호해져 컴파일 에러가 발생할 수 있습니다.
바로 이 암시적 형 변환이 문제입니다. if (t) 뿐만 아니라 int val = t; 와 같이 의도치 않은 상황에서도 형 변환이 일어나 버그를 유발할 수 있습니다.
3. 결론 및 다음 이야기
지금까지 C++ if문이 bool 외의 다른 타입을 조건으로 받아들이는 방식과, 클래스의 암시적 형 변환 연산자로 인해 발생할 수 있는 문제 상황을 살펴보았습니다.
이러한 의도치 않은 형 변환을 막기 위해 과거 C++ 개발자들은 Safe Bool Idiom과 같은 디자인 패턴을 사용했습니다. 그리고 C++11부터는 explicit 키워드를 통해 이 문제를 간결하고 안전하게 해결할 수 있게 되었습니다.
다음 글에서는 이 Safe Bool Idiom과 C++의 해법인 explicit operator bool()에 대해 조금 더 알아보겠습니다.
감사합니다.
<참고 자료>
1. C++ Understanding: 고급 Advanced
https://www.youtube.com/playlist?list=PLrrTotxaO6khn83BjtBN-1HMDc9MZ__yt
2. Effective Modern 이펙티브 모던 C++, Book
'Programming > C, C++' 카테고리의 다른 글
[C++] shared_ptr (1) - 필요성에 대해 알아보기 (0) | 2025.06.20 |
---|---|
[C++] Thread Local Storage (TLS)인 thread_local 대해 알아보기 (4) | 2025.06.12 |
[C++]컨테이너(Container)와 이터레이터(Iterator) 기초 2 (2) | 2025.05.23 |
[C++] 컨테이너(Container)와 이터레이터(Iterator) 기초 1 (10) | 2025.05.22 |
[C++] 타입 캐스팅 (static_cast, dynamic_cast) 대하여 (0) | 2025.05.10 |