C++에는 사실 '긴 화살표(long arrow)'라는 이름의 연산자는 존재하지 않습니다. 하지만 이 글에서 소개할 트릭을 알면, 마치 그런 연산자가 실제로 있는 것처럼 코드를 작성할 수 있습니다. 그 원리를 하나씩 살펴보겠습니다.
래퍼 타입과 operator->
C++에서는 때때로 래퍼(wrapper) 타입을 직접 만들어야 할 때가 있습니다. unique_ptr, shared_ptr, optional 같은 타입들이 대표적인 예입니다. 이런 타입들은 내부 값을 꺼내는 접근자(accessor) 멤버 함수인 .get()을 제공하는 동시에, 일반 포인터처럼 내부 값에 직접 접근할 수 있도록 operator->도 함께 제공합니다.
중첩된 래퍼 타입의 문제점
그런데 이런 래퍼 타입들이 서로 중첩되어 있는 경우가 종종 있습니다. 이럴 때는 값을 꺼내기 위해 .get()을 여러 번 호출하거나, 역참조 연산자를 여러 번 사용해야 합니다.
wrapper<wrapper<std::string>> wp;
wp.get().get().length();
wp.get()->length();
다소 지저분해 보이지 않나요? 하나의 .get()을 화살표(->)로 바꿀 수 있다면, 두 번째 .get()도 화살표로 바꿀 수 있다면 훨씬 깔끔할 것입니다.
'긴 화살표'의 등장
바로 이런 아이디어에서 출발한 것이 긴 화살표 표기법입니다.
wrapper<wrapper<std::string>> wp;
wp--->length();
그렇다면 래핑이 한 겹 더 있다면 어떻게 할까요? 간단합니다. 화살표를 더 길게 만들면 됩니다.
wrapper<wrapper<wrapper<std::string>>> wp;
wp----->length();
긴 화살표의 정체는 연산자의 조합
사실 긴 화살표는 단일 연산자가 아니라 여러 연산자가 결합된 형태입니다. 즉, 평범한 -> 연산자와 후위 감소(postfix decrement) 연산자 --의 조합입니다.
따라서 wp---->length()라고 작성하면 컴파일러는 이를 ((wp--)--)->length()로 해석합니다.
여기서 후위 -- 연산자가 역참조 연산자와 동일하게 동작하도록 정의하면, 긴 화살표는 물론 그보다 더 긴 화살표까지 자유롭게 만들어 낼 수 있습니다.
template <typename T>
class wrapper {
public:
T* operator->() { return &t; }
T& operator--(int) { return t; }
private:
T t;
};
마무리
결국 '긴 화살표 연산자'는 C++ 표준에 실제로 존재하는 기능이 아니라, ->와 후위 --의 파싱 규칙과 연산자 오버로딩을 활용한 재미있는 트릭입니다. 실무 코드에서는 가독성을 해칠 수 있으므로 사용을 권장하지는 않지만, C++가 연산자를 해석하는 방식을 이해하는 데는 아주 흥미로운 예제가 됩니다.