콘텐츠로 이동

별칭 (Aliases)

공개 별칭은 API 사용자를 위한 것이며, 명확하게 문서화되어야 합니다.

정의:

다른 엔터티의 별칭이 되는 이름을 만드는 방법은 여러 가지입니다.

using Bar = Foo;
typedef Foo Bar;  // 다만 C++ 코드에서는 `using`을 선호합니다.
using ::other_namespace::Foo;
using enum MyEnumType;  // MyEnumType의 모든 열거자에 대한 별칭을 만듭니다.

새 코드에서는 typedef보다 using이 낫습니다. C++의 나머지 부분과 더 일관된 문법을 제공하고 템플릿과도 함께 동작하기 때문입니다.

다른 선언과 마찬가지로, 헤더 파일에 선언된 별칭은 함수 정의 안이나 클래스의 private 부분, 또는 명시적으로 내부용이라고 표시된 네임스페이스에 있지 않는 한 그 헤더의 공개 API의 일부입니다. 그런 영역에 있거나 .cc 파일에 있는 별칭은 (클라이언트 코드가 참조할 수 없으므로) 구현 세부사항이며 이 규칙의 제약을 받지 않습니다.

장점:

  • 별칭은 길거나 복잡한 이름을 단순하게 만들어 가독성을 높일 수 있습니다.
  • 별칭은 API에서 반복해서 쓰이는 타입의 이름을 한곳에서 정함으로써 중복을 줄일 수 있고, 그 덕분에 나중에 타입을 바꾸기가 쉬워질 수 있습니다.

단점:

  • 클라이언트 코드가 참조할 수 있는 헤더에 별칭을 두면 그 헤더 API의 엔터티 수가 늘어나 복잡성이 커집니다.
  • 클라이언트가 공개 별칭의 의도치 않은 세부사항에 쉽게 의존하게 되어 변경이 어려워집니다.
  • API나 유지보수성에 미치는 영향을 고려하지 않은 채, 구현에서만 쓰려는 별칭을 공개로 만들고 싶은 유혹이 생길 수 있습니다.
  • 별칭은 이름 충돌의 위험을 만들 수 있습니다.
  • 별칭은 익숙한 구성물에 낯선 이름을 붙여 오히려 가독성을 떨어뜨릴 수 있습니다.
  • 타입 별칭은 API 계약을 불분명하게 만들 수 있습니다. 그 별칭이 원래 타입과 완전히 동일함이 보장되는지, 같은 API를 가진다는 것인지, 아니면 명시된 좁은 방식으로만 쓸 수 있다는 것인지가 분명하지 않습니다.

결정:

구현에서 타이핑을 줄이려는 이유만으로 공개 API에 별칭을 넣지 마세요. 클라이언트가 사용하기를 의도한 경우에만 그렇게 하세요.

공개 별칭을 정의할 때는 그 새 이름의 의도를 문서화하세요. 현재 별칭이 가리키는 타입과 항상 동일함이 보장되는지, 아니면 더 제한적인 호환성만 의도한 것인지를 포함해서 말입니다. 그래야 사용자가 두 타입을 서로 대체할 수 있는 것으로 다뤄도 되는지, 아니면 더 구체적인 규칙을 따라야 하는지 알 수 있고, 구현 쪽에서도 별칭을 바꿀 여지를 어느 정도 남길 수 있습니다.

공개 API에 네임스페이스 별칭을 넣지 마세요. (네임스페이스도 참조하세요.)

예를 들어 아래 별칭들은 클라이언트 코드에서 어떻게 쓰여야 하는지를 문서화하고 있습니다.

namespace mynamespace {
// 현장 측정값을 저장하는 데 사용합니다. DataPoint는 Bar*에서 어떤 내부
// 타입으로 바뀔 수 있습니다. 클라이언트 코드는 불투명 포인터로 취급해야 합니다.
using DataPoint = ::foo::Bar*;

// 측정값의 집합입니다. 사용자 편의를 위한 별칭일 뿐입니다.
using TimeSeries = std::unordered_set<DataPoint, std::hash<DataPoint>, DataPointComparator>;
}  // namespace mynamespace

아래 별칭들은 의도된 용도를 문서화하지 않으며, 그중 절반은 클라이언트가 쓰라고 만든 것도 아닙니다.

namespace mynamespace {
// 나쁨: 어느 것도 어떻게 쓰여야 하는지 말하지 않습니다.
using DataPoint = ::foo::Bar*;
using ::std::unordered_set;  // 나쁨: 그저 지역적인 편의를 위한 것
using ::std::hash;           // 나쁨: 그저 지역적인 편의를 위한 것
typedef unordered_set<DataPoint, hash<DataPoint>, DataPointComparator> TimeSeries;
}  // namespace mynamespace

다만 함수 정의 안, 클래스의 private 섹션, 명시적으로 내부용이라고 표시된 네임스페이스, 그리고 .cc 파일에서는 지역적인 편의 별칭을 써도 괜찮습니다.

// .cc 파일에서
using ::foo::Bar;

옮긴이 풀이

핵심: 공개 별칭은 사용자를 위한 것 — 문서화하라

별칭(using Bar = Foo;)을 만드는 방법은 여러 가지인데, 새 코드에서는 typedef보다 using을 쓰세요(문법이 일관되고 템플릿과도 잘 동작).

using Bar = Foo;            // 선호
typedef Foo Bar;            // 지양
using ::other_namespace::Foo;
using enum MyEnumType;

공개 vs 구현 세부

헤더의 (함수 안·클래스 private·internal 네임스페이스가 아닌) 별칭은 그 헤더 공개 API의 일부가 됩니다. 반면 .cc 파일이나 함수 안의 지역 편의 별칭은 구현 세부사항이라 이 규칙에 얽매이지 않습니다.

규칙

  • 단지 구현에서 타이핑을 줄이려고 공개 별칭을 만들지 마세요. 클라이언트가 쓰도록 의도한 경우에만.
  • 공개 별칭은 의도를 문서화하세요 — 별칭이 원래 타입과 항상 동일한지, 아니면 더 제한적으로 쓰여야 하는지.
  • 공개 API에 네임스페이스 별칭을 넣지 마세요.
namespace mynamespace {
// 측정값을 저장. DataPoint는 향후 내부 타입으로 바뀔 수 있으니
// 클라이언트는 불투명(opaque) 포인터로 취급할 것.
using DataPoint = ::foo::Bar*;
}  // namespace mynamespace