Abseil Tip 153 using-directives를 사용하지 마세요


title: “주간 팁 #153: using-directives를 사용하지 마세요” layout: tips sidenav: side-nav-tips.html published: true permalink: tips/153 type: markdown order: “153” —

원래 2018년 7월 17일에 TotW #153으로 게시됨

작성자: Roman PerepelitsaAshley Hedberg

2020-04-06 업데이트

빠른 링크: abseil.io/tips/153


“나는 using-directives를 시한폭탄으로 봅니다. 이를 사용하는 사람이나 언어 시스템 모두에게 위험합니다.”
Ashley Hedberg, 워렌 버핏을 빌려서


요약

using-directives (using namespace foo)는 Google 스타일 가이드에서 금지될 만큼 위험합니다. 향후 업그레이드가 필요한 코드에서는 이를 사용하지 마세요.

이름을 짧게 줄이고 싶다면, 대신 네임스페이스 별칭 (namespace baz = ::foo::bar::baz;)이나 using-선언 (using ::foo::SomeName)을 사용하세요. 스타일 가이드에서는 특정 문맥(예: *.cc 파일)에서 이를 허용합니다.


함수 범위에서의 using-directives

다음 코드는 무엇을 할까요?

namespace totw {
namespace example {
namespace {

TEST(MyTest, UsesUsingDirectives) {
  using namespace ::testing;
  Sequence seq;  // ::testing::Sequence
  WallTimer timer;  // ::WallTimer
  ...
}

}  // namespace
}  // namespace example
}  // namespace totw

대다수의 C++ 사용자들은 using-directive가 선언된 범위(이 경우 함수 범위)에 이름을 주입한다고 생각합니다. 하지만 실제로는 대상 네임스페이스(::testing)와 사용 네임스페이스(::totw::example::anonymous)의 가장 가까운 공통 조상 네임스페이스에 이름을 주입합니다. 위 예제에서는 전역 네임스페이스입니다!

결과적으로 코드는 다음과 비슷합니다:

using ::testing::Expectation;
using ::testing::Sequence;
using ::testing::UnorderedElementsAre;
...

namespace totw {
namespace example {
namespace {

TEST(MyTest, UsesUsingDirectives) {
  Sequence seq;  // ::testing::Sequence
  WallTimer timer;  // ::WallTimer
  ...
}

}  // namespace
}  // namespace example
}  // namespace totw

이 변환은 정확히 동일하지는 않습니다. 이름이 실제로 함수 범위 외부에서 계속 보이지는 않지만, 전역 범위에 임시로 주입되는 것만으로도 문제가 생길 수 있습니다.

어떤 변화가 이 코드를 깨뜨릴 수 있을까요?

  • ::totw::Sequence::totw::example::Sequence가 정의되면, seq는 더 이상 ::testing::Sequence를 참조하지 않을 수 있습니다.
  • ::Sequence가 정의되면, seq 정의가 컴파일되지 않을 수 있습니다. Sequence::testing::Sequence인지 ::Sequence인지 컴파일러가 모호해할 것입니다.
  • ::testing::WallTimer가 정의되면, timer 정의가 컴파일되지 않을 수 있습니다.

따라서 함수 범위에서의 단일 using-directive::testing, ::totw, ::totw::example, 전역 네임스페이스에 이름 충돌 위험을 만듭니다.


무자격 using-directives

함수 내에서 하나의 using-directive로 문제가 생긴다면, 여러 개의 무자격 using-directive를 사용하면 어떨까요?

namespace totw {
namespace example {
namespace {

using namespace rpc;
using namespace testing;

TEST(MyTest, UsesUsingDirectives) {
  Sequence seq;  // ::testing::Sequence
  WallTimer timer;  // ::WallTimer
  RPC rpc;  // ...이건 ::rpc::RPC인가, ::RPC인가?
  ...
}

}  // namespace
}  // namespace example
}  // namespace totw

무슨 일이 발생할 수 있을까요? 생각보다 많은 일이 생길 수 있습니다:

  1. 함수 수준 예제에서의 모든 문제가 여전히 발생하며, ::testing::rpc 각각에 대해 두 배로 문제가 생깁니다.
  2. ::rpc::testing 네임스페이스가 동일한 이름의 심볼을 선언하면, 이 코드는 컴파일되지 않을 수 있습니다.
  3. ::rpc::testing과 같은 하위 네임스페이스가 도입되면 이 코드는 컴파일되지 않을 가능성이 있습니다.

이 기능은 왜 존재할까요?

using-directives는 제네릭 라이브러리 내에서 드물게 합법적으로 사용될 수 있지만, 매우 희소한 경우라 여기나 스타일 가이드에서 다룰 필요조차 없습니다.


결론

using-directives시한폭탄입니다. 오늘 컴파일되던 코드가 언어 버전 변경이나 심볼 추가로 인해 쉽게 컴파일되지 않을 수 있습니다. 외부 코드가 단명하고 종속성이 변경되지 않는다면 이는 감수할 수 있는 위험일 수 있습니다. 그러나 장기적으로 유지보수가 필요한 프로젝트에서는 이 시한폭탄이 언젠가 터질 가능성이 큽니다.