Skip to content

In: 소개

이 핵심 가이드라인은 모던 C++ (현재는 C++17) 및 추후 개선될 내용과 ISO 기술 명세(Technical Specification; TS)를 고려한 것이다. 가이드라인의 목표는 C++ 프로그래머들이 더 간단하고, 효과적이며, 유지보수하기 좋은 코드를 작성하도록 돕는 것이다.

소개 요약:

In.target: 대상 독자

모든 C++ 프로그래머. 또한 C를 생각 중인 프로그래머.

In.aims: 목표

핵심 가이드라인의 목적은 개발자들이 모던 C++ (현재는 C++17)을 받아들이고 보다 일관적인 형태의 스타일로 코드를 작성하도록 하는 것이다.

이 문서에서 다루고 있는 규칙들이 모든 코드에 효과적으로 적용할 수 있다고 생각하지는 않는다. 오래된 시스템을 새롭게 업그레이드하는 것은 쉽지 않은 일이다. 하지만 여기서 다루는 규칙들을 적용한다면 기존의 방식보다 오류가 발생할 가능성이 낮고, 유지 보수가 편리한 코드를 작성할 수 있을 것이라 확신한다. 또한 이러한 규칙들은 개발 초기에 좀 더 빠르고 쉽게 개발을 진행하는 데도 도움이 될 것이다. 분명히 말할 수 있는 것은, 이러한 규칙들을 적용하면 제로-비용 원칙(Zero-overhead principle)에 입각해 이전보다 더 나은 동작을 수행되는 코드를 만들 수 있다는 점이다. (제로-비용 원칙이란 "사용하지 않는 부분에 비용을 낭비하지 마라.", 또는 "올바른 추상화 메커니즘을 사용했을 경우, 적어도 저수준 언어로 하드코딩한 것 만큼의 성능을 얻을 수 있다." 정도로 설명할 수 있다.) 새로운 코드를 작성하거나 기존 코드를 개선할 여지가 생겼다면, 적용 가능한 수준에서 규칙들을 적용해 보기 바란다.

In.0: 당황하지 마라!

여러분의 프로그램에 가이드라인에 나온 규칙을 적용한다면, 프로그램에 어떤 영향을 줄 지 충분히 고려해야 한다.

가이드라인은 "상위 집합의 하위 집합(Subset of superset)" (Stroustrup05) 원리에 따라 구성되어 있다. 우리는 신뢰성, 안정성, 성능 등을 고려해 단순히 C++의 한 부분 집합을 정의하지 않는다. 대신, 몇 가지 간단한 "확장" (가이드라인 지원 라이브러리)을 사용하길 강력히 권고한다. 이를 통해 오류가 발생하기 쉬운 C++ 기능들의 대부분을 제거할 수 있다.

규칙들은 정적 타입 안정성과 리소스 안정성에 주안점을 두고 있다. 이러한 이유로 범위 확인 가능성, nullptr를 통한 역참조 회피 가능성, 댕글링 포인터(Dangling pointer) 회피 가능성, (RAII를 통한) 시스템적인 예외 사용 등을 강조한다. 잘 알려져 있지 않은 소스 코드의 오류 발생 가능성을 부분적으로나마 극복하고 최소화 하는 방법, 좀 더 단순한 표현 방식뿐 아니라 올바르게 정의된 인터페이스를 통해 복잡도를 드러내지 않는 방법 등을 강조한다.

많은 규칙들은 규범적인 성격을 띄고 있다. 대안도 없이 그저 "그렇게 하지 마라!"라고만 하는 규칙들은 불편할 수 밖에 없다. 일부 규칙들은 기계적으로 정밀 검증할 수 있다기 보다는 경험에 근거하여 작성되었다. 다른 부류의 규칙들은 일반적인 원칙을 논리적으로 정리한 것이며, 널리 사용될 것이라 생각되는 규칙들은 더욱 자세하게, 그리고 부분적으로나마 검증이 가능하도록 구체적으로 설명했다.

또한 이 가이드라인은 C++에서 핵심이 되는 내용과 그 사용법을 다루고 있기도 하다. 조직의 규모가 매우 크거나, 특화된 분야의 애플리케이션을 개발하거나, 프로젝트의 규모가 매우 크다면 여기서 다루는 내용보다 더 다양한 규칙과 제약, 라이브러리가 필요할 것이다. 예를 들어 고도의 실시간 애플리케이션을 개발하는 경우라면 자유 저장소(Free Store, 동적 메모리)를 아무렇게나 사용하면 안되기 때문에, 라이브러리를 선택하는데 제약이 있을 수 밖에 없다. 이처럼 특화된 개발 분야에만 적용 할 수 있는 규칙들은 핵심 가이드라인의 부록에 담았다. 어셈블리 코드와 같이 저수준의 프로그래밍 방식을 고수하기 보다는 핵심 기능을 구현하고 있는 소규모의 라이브러리를 만들고 사용하기 바란다.

규칙들은 점진적으로 적용해 볼 수 있다.

일부 규칙들은 안정성을 높이기 위해 다양한 방법들을 설명하고 있으며, 또 다른 규칙들은 문제 발생 가능성을 낮추는 방법을 설명하고 있다. 혹은 이 둘을 모두 고려해 만들어진 규칙들도 있다. 사고를 예방하기 위한 가이드라인이 때로는 합법적인(legal) C++에 반대되기도 한다. 규칙을 통해 전달하고자 하는 내용을 기술할 때 통상 오류 발생 가능성이 높은 경우와 그렇지 않은 경우가 있다면, 가능한 오류 발생 가능성이 낮은 방법을 택했다.

In.not: 목표가 아닌 것들

최소한의 규칙만을 정의하거나, 규칙들이 서로를 위반하지 않도록 작성한 것은 아니다. 실제로 범용적인 규칙들 중에는 간단해 보이지만 실제로 적용하기가 매우 어려운 것들도 있으며, 그러한 규칙들이 지닌 함축적인 의미를 이해하기 어려울 수도 있다. 구체적인 규칙들이 좀 더 이해 및 적용하기 쉽지만, 범용적인 규칙들을 다루지 않고는 특별한 경우만을 장황하게 나열할 수 밖에 없었을 것이다. 각각의 규칙들은 초보 개발자 뿐만 아니라 전문가들에게도 도움이 될 수 있도록 작성했다. 일부 규칙은 반드시 적용되어야 하지만, 필요에 따라 적용 여부를 선택적으로 결정해야 하는 경우도 있다.

여기서 다루는 규칙들을 책에서 다루는 것인냥 너무 심각하게 받아들을 필요는 없다. 링크를 통해서 규칙들을 가볍게 살펴보는 것도 괜찮다. 사실 이처럼 규칙을 엄밀하게 정의한 이유는 규칙들을 위배한 코드를 찾고, 그에 대한 링크 정보를 보여주는 툴을 만들기 위해서이다. 즉, 규칙이 만들어진 이유를 설명하고, 만약 규칙을 따르지 않았을 때 어떤 문제가 발생할 수 있고, 그 문제를 어떻게 해결할 수 있는지를 알려주기 위해서다.

이 가이드라인은 C++ 튜토리얼을 대체할 용도로 작성된 것이 아니다. 개발자의 수준에 부합하는 튜토리얼이 필요하다면, 참고 문헌을 참조하기 바란다.

이 문서는 기존의 C++ 코드를 모던 C++ 코드로 변환하는 방법에 대해서 다루고 있는 것도 아니다. 다만, 새로운 C++ 코드에 대한 논리 정연한 생각들을 구체적으로 설명한다. 따라서 기존 코드를 모던하게, 젊고 활기차게, 업그레이드 하고 싶다면 모던 C++ 코드를 참조하기 바란다. 중요한 것은 이 문서에서 다루고 있는 규칙들을 점진적으로 적용할 수 있다는 점이다. 엄청난 양의 코드를 단번에 바꿀 수는 없는 노릇이다.

이 가이드라인이 언어의 기술 세부 사항을 완벽하게, 또는 정확하게 설명하지는 않는다. 언어의 명세, 일반 규칙에 대한 예외 사항, 그 외 세부 기능들에 대해서는 ISO C++ 표준을 참조하기 바란다.

규칙들을 작성할 때 C++의 일부 기능만을 이용해 코드를 작성하도록 의도하지는 않았다. 마치 C++의 일부만을 떼어놓은 Java를 사용하는 것처럼 강제하기 위함은 절대로 아니라는 것이다. "단 하나의 올바른 C++" 언어라는 식의 정의 또한 의도하는 바가 아니다. 다만 가이드라인에 포함된 규칙을 통해서 C++라는 언어가 성능과 타협하지 않으면서도 풍부한 표현력을 지닌 언어라는 점을 강조하고자 했다.

세부 규칙들을 통해서 전달하려는 가치는 명확하다. 기존의 C++ 코드보다 더욱 간단하고, 올바르고, 안전한 코드를 성능을 손해보지 않고 작성할 수 있도록 돕는 것이다. 또한 유효한 C++ 코드이긴 하지만 오류를 유발할 가능성이 높고, 불필요하게 복잡하고, 성능도 좋지 않은 코드를 피할 수 있도록 돕는 것이다.

규칙들이 완벽하지는 않다. 규칙이 주어진 상황에서 유익한 요소를 금지할 수도 있다. 규칙이 주어진 상황에서 중대한 오류가 발생하는 것을 막지 못할 수도 있다. 규칙이 모호하고, 실행 불가능하고, 문제를 위한 모든 해결책을 열어놓음으로써 더 많은 해로움을 가져올 수 있다. "어떠한 해로움도 없는" 기준을 완벽하게 만족하는 것은 불가능하다 우리의 목표는, 대신, "다수의 프로그래머들에게 가장 좋은 것"을 위한 기준을 제시하는 것이다.
규칙을 원하지 않는다면, 따르지 않거나 무시해도 좋다. 하지만 규칙이 무의미해지기 전까지는 폄하하지 말라. 또 가능하다면, 개선 사항을 제안하기를 바란다.

In.force: 가이드라인 적용

사실상 다양한 규칙들을 적용하도록 강제하지 않고서는 이러한 규칙들이 방대한 코드에 모두 적용되리라고 기대하기는 어렵다. 실상, 모든 규칙을 강제적으로 적용하는 것은 규칙의 수가 몇 개 되지 않거나 특수한 사용자 집단에서나 가능한 일일지도 모르겠다.

  • 그러나 사람들은 모든 사람들이 사용할 수 있는 다양한 규칙들을 원한다
  • 그러나 사람들은 서로 다른 규칙들을 갖는다
  • 그러나 사람들은 방대한 규칙을 모두 읽고 싶어하지 않는다
  • 그러나 사람들은 수많은 규칙들을 기억할 수 없다

이러한 이유로 다양한 요구 사항들의 공통적인 부분만을 뽑아내려고 해봤다.

  • 그러나 임의의 규칙을 정하는 것 조차 혼돈을 초래할 것이다

우리는 더 많은 개발자들에게 도움이 되고, 코드를 좀 더 간결하게 만들 수 있으며, 기존 코드를 모던화 할 수 있는 가이드라인을 만들고 싶었다. 개인의 선택의 문제라거나 관리의 압박으로 인해 신경쓰지 않았던 부분들도 도외시하지 않고 최선의 실용적인 예를 다루고자 했다. 바라건대 모든 규칙들을 적용하는 것이 좋다고 생각한다. 그렇게 해야만 최고의 이득을 본다고 생각하기 때문이다.

이는 꽤나 심각한 딜레마가 아닐 수 없는데, 우리는 이러한 딜레마의 해결책이 툴을 개발하는 것이라고 생각했다. 각각의 규칙들은 적용 방법을 설명하고 있는 Enforcement 단락을 갖고 있는데, 코드 리뷰, 정적 분석, 컴파일러, 런타임 체크 등의 방법을 나열하고 있다. 어떤 방식이든 우리는 "기계적"이며(사람은 느리기도 하고, 쉽게 지루해 할 수 있으므로) 일관된 방법으로 개별 규칙들이 적용되기를 원했다. 이런 이유로 런타임 체크는 다른 대안이 없을 경우에 한해서만 언급했다. 이 같은 내용들을 여기저기에 흩어놓기 보다는 사용자가 원할 경우 쉽게 찾을 수 있도록 하고 싶었기에, (Enforcement 단락 내에) 적절한 위치라고 생각되는 곳에 연관된 규칙들을 "프로필"이라는 이름으로 나열해 두었다. 하나의 규칙은 여러 프로필에 속할 수 있으며, 어떤 프로필에도 속하지 않은 규칙들도 있다. 자주 사용되는 프로필 몇 가지를 먼저 살펴보자.

  • 타입: (캐스팅, 공용체, 가변 인수를 통해 TU로 재해석 하는) 타입 위반 없음
  • 한계: (배열의 범위를 넘어서 접근하는) 범위 위반 없음
  • 수명: (deletedelete[]이 실패하는) 누수 없음, (nullptr를 역참조하고 댕글링된 참조를 사용해) 유효하지 않은 개체에 접근하지 않음

프로필은 툴에서 활용할 용도로 만들었지만, 내용을 읽어보면 많은 도움이 된다. 적용 단락을 단순히 우리가 알고 있는 적용 방법을 제시하는 것으로만 제한하고 싶지 않다. 단락 중 일부는 툴 개발자에게 도움이 될만한 내용일 것이다.

가이드라인의 규칙들을 구현한 툴에서는 명시적으로 규칙을 무시하는 다음과 같은 문법을 지원하기를 바란다:

    [[gsl::suppress(tag)]]

여기서 "tag"는 HTML anchor의 이름으로, 가이드라인 내에서 규칙이 있는 지점을 의미한다. (가령, C.134 규칙의 경우, Anchor 값은 "Rh-public"이다. )

In.struct: 문서의 구조

각 규칙(가이드라인, 제안)은 여러 부분으로 나뉜다.

  • 규칙 그 자체 -- 예) new를 무방비 상태로 사용하지 마라
  • 규칙 참조 번호 -- 예), C.7 (클래스와 관련된 7번째 규칙). 주요 단원이 순서대로 나열되지 않았기 때문에 규칙 참조 "번호"의 첫 번째 부분은 문자로 시작한다. 규칙을 추가하거나 삭제할 때 혼란을 최소화하기 위해 숫자를 매길때 간격을 둔다.
  • 이유들 (근거) -- 이해할 수 없는 규칙을 따르고 싶지는 않을 것이므로 구체적인 이유를 설명한다.
  • 예제들 -- 추상적인 표현만으로 내용을 이해하기 어려울 경우 구체적인 예(좋은 예나 나쁜 예)를 기술한다.
  • 대안들 -- "이런 식으로 하지 마라"에 대한 대안을 제시한다.
  • 예외들 -- 단순하고 보편적인 규칙이 좋다. 많은 규칙들이 널리 사용되지만 만능은 아니므로 예외가 있을 경우 이를 나열한다.
  • 적용 -- "기계적으로" 규칙을 확인하는 방법에 대한 아이디어를 설명한다.
  • 참고할 만한 내용들 -- (이 문서나 다른 문서에 대해) 규칙이나 관련 내용과 연관된 항목들을 안내한다.
  • 비고들 (언급) -- 다른데서 다루기 적합하지 않은 부분에 대해 추가로 설명한다.
  • 논의 -- 규칙을 제시한 근본적인 이유를 담고 있는 다른 글에 대한 참조나 규칙의 주요 리스트에 포함되지 않는 예 등을 설명한다.

일부 규칙들은 기계적으로 확인하기에 어려울 수 있으나 전문적인 프로그래머라면 손쉽게 위반 여부를 발견할 수 있다. "기계적인" 도구들이 그런 전문 프로그래머 처럼 지적할 수 있도록 발전하기를 희망한다. 또, 규칙들이 시간이 지날수록 더 정확하고 검사가 가능하도록 발전하도록 할 것이다.

각 규칙들이 가능한 단순하게 유지되길 바란다. 적용 가능한 모든 대안과 특별한 예외사항까지 모두 언급하기를 바라지는 않는다. 이러한 정보는 대안 단락과 토론 절에서 찾을 수 있다. 규칙을 이해할 수 없거나 동의하지 않는다면, 논의를 살펴보기 바란다. 또한, 논의가 없거나 불완전하다고 생각된다면 Issue에 여러분이 염려하는 부분과 가능하다면 관련 PR에 대한 설명을 적어주기 바란다.

이 문서는 언어어 대한 매뉴얼이 아니다. 따라서 기술적인 세부 사항을 자세히 다루기보다는 기존에 작성된 코드에 대한 가이드로써의 역할을 했으면 한다. 도움이 되는 정보의 출처는 참고 문헌에서 찾을 수 있다.

In.sec: 주요 목차

참고할 만한 내용:

각 부분들은 서로 연관되어 있다.

각 목차("P"는 "철학(Philosophy)")와 부 목차("C.hier"는 "클래스 계층(Class Hierarchies) (OOP)")은 검색, 참조의 편의를 위해 약어로 표기한다. 주요 목차에 대한 약어로 규칙 번호를 사용하기도 한다. (즉, "각 타입을 일반적인 타입으로 만들어라."라는 규칙을 "C.11"로 나타내기도 한다.)