P.4: Ideally, a program should be statically type safe
이유
C++ 는 데이터 타입을 컴파일 시점에 미리 알 수 있는 statically typed language 입니다.
컴파일러가 타입 오류 (형변환 실패, 정밀도 손실 등) 를 미리 감지하도록 가급적 컴파일 타임 타입을 사용하면 프로그램의 안정성을 미리 확보할 수 있습니다.
하지만 안타깝게도 아래와 같은 요소들로 인해 컴파일 타임에 그 타입을 알기 어렵게 만듭니다.
1. unions (공용체, 같은 메모리 공간을 여러 타입으로 취급)
2. casts (단순 형변환)
3. array decay (배열이었으나 단순 포인터로 취급)
4. range errors (메모리 범위를 벗어난 접근)
5. narrowing conversions (암묵적인 변환으로 인한 정밀도 손실)
알아두기
위 요소들을 잘못 사용하였을 때 런타임에 보안 위배나 크래쉬를 발생시킬 수 있습니다.
현대 C++ 에서는 아래와 같은 대안으로 이 문제를 해결합니다.
정리
1. unions – std::variant (C++17) 를 사용하세요.
2. casts – 사용을 가급적 줄이고, 템플릿을 사용하여 해결하세요.
3. array decay – std::span (C++20) 을 사용해서 C 배열의 크기를 얻고 범위 오류도 피하세요.
4. range errors – 마찬가지로 std::span (C++20) 를 사용하세요.
예시 (나쁜 예)
// n 개의 요소가 있는 p 배열 안에서 최대값을 찾아 반환하는 함수가 있다고 가정합니다
int GetMax(int* p, int n);
int a[100];
// 이렇게 실수하면 범위를 벗어나 접근하므로 segmentation fault 가 발생합니다
GetMax(a, 1000);
이렇게 불필요한 for 문을 돌리지 않고도 static_assert 를 사용하면 컴파일 타임에 간단하게 int 크기를 검증할 수 있습니다.
예시 (좋은 예)
// 배열을 std::span<int> 타입으로 받고 있습니다
int getMax(std::span<int> r);
int a[100];
// 배열의 크기를 컴파일러가 알아내도록 맡기면 효율적이고 코드가 간결해집니다
getMax(a);
5. narrowing conversions – 가급적 정밀도가 손실되는 변환을 피하고 GSL 의 narrow 또는 narrow_cast 를 사용하세요. 그리고 중괄호를 활용하면 컴파일러가 이러한 정밀도 손실을 미리 경고해줄 수 있습니다.
int var1 {3.14159}; // warning!
참고자료
'C++' 카테고리의 다른 글
[C++ Core Guidelines] P.6 컴파일 타임에 평가가 불가능한 코드는 런타임에서라도 평가되도록 하자 (0) | 2024.04.11 |
---|---|
[C++ Core Guidelines] P.5 가급적 컴파일 타임에 코드가 평가될 수 있도록 만들자 (0) | 2024.04.11 |
[C++ Core Guidelines] P.2 ISO 표준 C++ 코드로 작성하자 (0) | 2024.03.30 |
[C++ Core Guidelines] P.1, P.3 생각을 코드로 직접 나타내자 (2) | 2024.03.27 |
[C++ Core Guidelines] 번역에 앞서 (1) | 2024.03.26 |