James Dennett (jdennett@google.com) 작성
최초 게시일: 2019년 4월 16일
최종 업데이트: 2020년 4월 6일

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


“우리는 전역적으로 당황할 수 있지만, 지역적으로 고통받는다.”
– 조너선 프랜즌


개요

지역 변수는 매우 유용하지만, 과도하게 사용될 수 있습니다. 지역 변수는 특정 이점을 제공하는 상황으로 사용을 제한함으로써 코드의 복잡성을 줄일 수 있습니다.


권장 사항

다음 중 하나 이상의 조건에 해당하는 경우에만 지역 변수를 사용하세요:

그 외의 경우, 지역 변수를 제거하고 표현식을 직접 사용하여 간접성을 줄이는 것이 좋습니다.


근거

값에 이름을 지정하면 코드 이해에 있어 간접적인 계층이 추가되며, 변수의 이름이 관련된 의미를 완전히 포착하지 못하는 경우 더욱 그렇습니다. 또한 C++에서 값에 이름을 지정하면 해당 이름이 범위 전체에 노출됩니다. 모든 이름이 지정된 변수는 rvalue 참조로 선언되고 rvalue로 초기화되더라도 lvalue가 되기 때문에 “값 범주"에도 영향을 미칩니다. 이는 std::move의 추가 사용을 요구할 수 있으며, 코드 리뷰 시 이동 후 사용(use-after-move) 오류를 피하기 위해 신중을 기해야 합니다. 이러한 단점으로 인해 지역 변수는 특정 이점을 제공하는 경우에만 사용하는 것이 가장 좋습니다.


나쁜 지역 변수 사용 예시

즉시 반환되는 지역 변수 제거

다음과 같은 코드 대신:

CPP
MyType value = SomeExpression(args);
return value;
클릭하여 더 보기

다음과 같이 작성하는 것이 좋습니다:

CPP
return SomeExpression(args);
클릭하여 더 보기

GoogleTest의 EXPECT_THAT에서 표현식을 인라인 처리하기

다음은 나쁜 코드의 예입니다:

CPP
std::vector<string> actual = SortedAges(args);
EXPECT_THAT(actual, ElementsAre(21, 42, 63));
클릭하여 더 보기

여기서 actual이라는 변수명은 유용한 정보를 추가하지 않으며, 복잡한 표현식을 단순화하지 않고, 값이 한 번만 사용됩니다. 아래와 같이 표현식을 인라인 처리하는 것이 더 좋습니다:

CPP
EXPECT_THAT(SortedAges(args), ElementsAre(21, 42, 63));
클릭하여 더 보기

이는 테스트하려는 내용을 한눈에 명확히 보여줄 뿐만 아니라, actual이라는 이름을 사용하지 않음으로써 의도치 않게 다시 사용되는 것을 방지합니다.


테스트에서 Matcher 사용으로 지역 변수 제거

다음과 같은 코드:

CPP
std::optional<std::vector<int>> maybe_ages = GetAges(args);
ASSERT_NE(maybe_ages, std::nullopt);
std::vector<int> ages = maybe_ages.value();
ASSERT_EQ(ages.size(), 3);
EXPECT_EQ(ages[0], 21);
EXPECT_EQ(ages[1], 42);
EXPECT_EQ(ages[2], 63);
클릭하여 더 보기

대신 아래와 같이 작성할 수 있습니다:

CPP
EXPECT_THAT(GetAges(args), Optional(ElementsAre(21, 42, 63)));
클릭하여 더 보기

이 방식은 테스트 의도를 더 직접적으로 표현할 수 있습니다.


좋은 지역 변수 사용 예시

반복되는 표현식 추출

다음과 같은 코드:

CPP
myproto.mutable_submessage()->mutable_subsubmessage()->set_foo(21);
myproto.mutable_submessage()->mutable_subsubmessage()->set_bar(42);
myproto.mutable_submessage()->mutable_subsubmessage()->set_baz(63);
클릭하여 더 보기

에서 반복되는 표현식을 추출하면 코드가 더 간결해지고 가독성이 높아집니다:

CPP
SubSubMessage& subsubmessage = *myproto.mutable_submessage()->mutable_subsubmessage();
subsubmessage.set_foo(21);
subsubmessage.set_bar(42);
subsubmessage.set_baz(63);
클릭하여 더 보기

Pair와 Tuple 요소에 의미 있는 이름 부여

pairtuple 대신 의미 있는 필드 이름을 가진 struct를 사용하는 것이 좋지만, 의미 있는 이름을 가진 별칭을 요소에 바인딩하여 문제를 완화할 수 있습니다. 예를 들어:

CPP
for (const auto& name_and_age : ages_by_name) {
  if (IsDisallowedName(name_and_age.first)) continue;
  if (name_and_age.second < 18) children.insert(name_and_age.first);
}
클릭하여 더 보기

를 다음과 같이 작성할 수 있습니다:

CPP
for (const auto& [name, age] : ages_by_name) {
  if (IsDisallowedName(name)) continue;
  if (age < 18) children.insert(name);
}
클릭하여 더 보기

이로써 가독성이 향상됩니다.


위 내용이 여러분의 C++ 코드 품질 향상에 도움이 되길 바랍니다! 😊

라이선스

저작자: Jaehun Ryu

링크: https://jaehun.me/posts/abseil-tip-161-%EC%A2%8B%EC%9D%80-%EC%A7%80%EC%97%AD-%EB%B3%80%EC%88%98%EC%99%80-%EB%82%98%EC%81%9C-%EC%A7%80%EC%97%AD-%EB%B3%80%EC%88%98/

라이선스: CC BY 4.0

이 저작물은 크리에이티브 커먼즈 저작자표시 4.0 국제 라이선스에 따라 이용할 수 있습니다. 출처를 밝히면 상업적 목적을 포함해 자유롭게 이용 가능합니다.

댓글

검색 시작

검색어를 입력하세요

↑↓
ESC
⌘K 단축키