[모던 C++ #3] 매크로 상수를 버리고 constexpr와 enum class로!

과거 C++98/03 시절에는 정적 상수를 정의할 때 매크로를 사용하는 경우가 흔했습니다. 매크로는 전처리 단계에서 단순한 텍스트 치환을 수행하므로, 타입 정보가 없고 디버깅도 까다롭다는 문제가 있었습니다. 또한 범위(scope) 개념이 없어 어디서나 무분별하게 확장되며, 심지어 이름 충돌 문제도 야기할 수 있었습니다.

모던 C++에서는 이러한 문제를 해결하기 위해 constexpr, const 정적 상수, 그리고 enum class를 적극적으로 활용할 수 있습니다. 이를 통해 타입 안전성과 가독성을 향상시키고, 컴파일 시간 상수 평가를 통한 성능 최적화도 기대할 수 있습니다.

관련 참고 자료:

과거: 매크로 상수의 문제점

C-Style 매크로를 통해 상수를 정의하면, 이는 그저 문자열 치환에 불과합니다. 타입 정보가 없어 사용자가 실수로 잘못된 타입으로 사용해도 컴파일러는 이를 체크하지 못하고, 디버깅 시에도 어디서 어떻게 치환되었는지 추적하기 어렵습니다.

#include <iostream>

#define SIZE 100
#define SQUARE(x) (x * x)

int main() {
    int arr[SIZE]; // SIZE는 단순히 100으로 치환
    std::cout << SQUARE(5) << "\n"; // (5 * 5)로 치환되어 25 출력

    // 실수: 매크로로 인한 예기치 않은 평가
    std::cout << SQUARE(1+2) << "\n"; 
    // (1+2 * 1+2) 로 치환 -> 1+2 * 1+2 = 1+2*1+2 = 1+2+2 = 5 출력 
    // 의도한 9와 다름. 괄호 처리 실수 시 이런 문제 발생.
    return 0;
}

위 예제에서 SQUARE(1+2)는 (1+2 * 1+2)로 치환되어 연산 우선순위를 잘못 처리하게 됩니다. 매크로는 이런 식으로 작은 실수가 심각한 버그로 이어질 수 있습니다.

현재: constexpr, enum class를 통한 안전한 상수 표현

constexpr를 통한 타입 안전 정적 상수

constexpr 키워드는 변수를 컴파일 시간 상수로 명시할 수 있습니다. 이는 컴파일러가 해당 값을 컴파일 시간에 평가할 수 있음을 의미하며, 타입 정보도 유지됩니다.

#include <iostream>

constexpr int SIZE = 100;

constexpr int square(int x) {
    return x * x;
}

int main() {
    int arr[SIZE]; // 컴파일 타임에 100으로 평가
    std::cout << square(5) << "\n"; // 컴파일 타임에 25로 평가 가능
    std::cout << square(1+2) << "\n"; // 9로 정확히 계산
    return 0;
}

constexpr를 사용하면 상수가 타입 안전하게 정의되며, 매크로보다 훨씬 직관적이고 디버깅하기 쉽습니다.

enum class로 명확한 열거형 타입 사용

과거 C 스타일 열거형 enum은 전역 네임스페이스에 정수 상수를 방출하고, 범위와 타입 안전성이 부족했습니다. C++11 이후에는 enum class를 통해 열거형을 독립된 타입으로 다루고, 명확한 스코프를 부여할 수 있습니다.

#include <iostream>

enum class Color : int {
    Red = 0,
    Green = 1,
    Blue = 2
};

int main() {
    Color c = Color::Red;
    // 정수 캐스팅 필요, 명시적이어서 실수 방지
    std::cout << static_cast<int>(c) << "\n"; // 0 출력
    return 0;
}

enum class를 사용하면 열거형 값이 고유한 타입으로 취급되며, 다른 타입과 혼동될 위험이 줄어듭니다. 이는 코드의 의도를 명확히 전달하고, 타입 안전성을 향상시킵니다.

왜 이런 변화가 필요한가?

  1. 타입 안전성 확보
    매크로 상수 사용 시 타입 정보가 사라지며, 실수나 오타가 런타임 버그로 이어질 수 있습니다. constexpr와 enum class는 타입 시스템을 통해 실수를 사전에 방지합니다.
  2. 가독성과 유지보수성 개선
    매크로 상수 대신 constexpr를 사용하면 상수 정의가 코드 맥락 안에서 명확히 드러나며, enum class는 열거형 값을 스코프에 묶어둠으로써 네임스페이스 오염을 줄이고, 읽기 쉬운 코드를 작성할 수 있습니다.
  3. 컴파일 타임 최적화
    constexpr는 컴파일 타임에 값이 결정되므로, 런타임 오버헤드를 줄이고 성능 최적화를 이끌어냅니다.
반응형