C++11 기능 소개: 중첩 '>' 문제 해결

Posted at 2012. 4. 10. 12:30 // in S/W개발/C++11 // by 김윤수


여러 말로 설명하기 보다 관련 예제부터 한 번 보겠습니다.

map<string,vector<int>> heroChronicles;

C++98에서는 위 코드가 컴파일되지 않습니다. 왜냐하면 “>>”이 연속된 ‘>’으로 인식되는 것이 아니라 구문 분석 시작 전 어휘 분석 단계(lexical analysis)에서 최대 흡수(Maximal munch) 규칙에 따라  “>>” 토큰으로 인식되고 이는 다시 문법 단계에서 “>>” 연산자로 인식되고, ">>" 연산자 좌우로 int, heroChronicles 같은 토큰들이 있는 것으로 인식되기 때문입니다. 템플릿 초심자들이 많이 저지르는 실수이죠. 그래서 C++98에서는 다음과 같이 작성해야 했습니다.

map<string,vector<int> > heroChronicles;

이 뿐 아니라 새로운 형태의 형변환 연산자도 비슷한 문제가 있습니다.

static_cast<List<B>>(ld); 

  // static_cast<List<(B>>(ld)); 로 해석

>>= 및 >= 도 비슷한 문제가 발생할 수 있습니다.

void func(List<B>= default_val1);

  // 원래 의도는 List<B> lb = default_val1이나 
// List<(B >= default_val1)); 으로 해석

void func(List<List<B>>= default_val2);
  // 원래 의도는 List<List<B>> llb = default_val2이나
// List<List<(B>>= default_val2)); 으로 해석

템플릿이 많이 사용되면 많이 사용될 수록 이런 코드는 더 큰 문제가 될 것입니다.

애초에 C++98에서 이런 문제가 발생했던 것은 컴파일러가 이론적으로 어휘분석, 문법분석, 형식체크와 같이 몇 단계를 거쳐 처리하므로
이 문제를 해결하기 위해서는 어휘 분석 단계가 문법 분석 단계와 협력해야 하는 문제가 발생하게 되기 때문이었습니다. 결국 컴파일러 구현자들을 위한 결정이었던 것이죠.

그런데 시중에 출시된 C++ 컴파일러들이 이 문제를 인식하고 친철한 에러 메시지를 출력하고 있다는 것이 표준화를 진행하는 동안 밝혀졌습니다.

예를 들어, g++에서는 다음과 같은 에러 메시지를 출력하고 있었습니다.

error: ‘>>’ should be ‘> >’ within a nested template argument list

이 정도로 이미 컴파일러들이 이 문제를 나름대로 인식하고 해결하고 있었기 때문에 C++11에서는 어휘 분석 단계와 문법 분석 단계가 협력하여 문제를 해결하도록 표준에 명시하기에 이르렀습니다. 기본적으로 다음과 같은 규칙을 따르도록 되어 있습니다.

왼쪽 ‘<’ 이 ‘>’과 서로 매치가 되어 있지 않은 동안에는 >> 토큰은 오른쪽 shift 연산자가 아닌 두 개의 연속된 ‘>’로 인식되어야 한다.

위와 같은 규칙은 어휘 분석 단계와 문법 분석 단계가 긴밀하게 협력할 필요 없이 문법 분석 단계에서 간단한 검사만 하면 구현할 수 있을 정도이므로 컴파일러 구현에도 큰 문제가 되지는 않을 것입니다.

항상 그렇듯 이 규칙에도 예외가 있습니다.

다음 예제를 보시죠.

Y<X<6>>1>> x4;

코드를 보고 예측해 보건데 프로그래머는 6>>1의 결과값을 X 템플릿의 인자로 주고 싶었을 것입니다. 즉, 다음과 같은 의미였겠죠.

Y<X<(6>>1)>> x4;

이런 코드가 문제 없이 컴파일되도록 하기 위해서는 괄호 안에서는 위와 같은 규칙이 적용되지 않도록 해야하므로 다음과 같이 예외조항을 두어야 할 것입니다.

왼쪽 ‘<’ 이 ‘>’과 서로 매치가 되어 있지 않은 동안에는 >> 토큰은 오른쪽 shift 연산자가 아닌 두 개의 연속된 ‘>’로 인식되어야 한다. 단, 괄호쌍이나 대괄호쌍에 있는 >>는 예외이다.

이 규칙이 C++11에서 정하고 있는 것과 정확히 동일합니다. 이제 C++ 초심자들에게 템플릿을 가르칠 때, 중첩 템플릿 인자를 명시할 때는 반드시 한 칸을 띄어야 한다고 설명하면서 "왜 띄어야 하느냐?"라는 질문을 받을까봐 노심초사하는 일은 없어도 될 것 같습니다. ^^;


참고문헌

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1757.html
http://occamsrazr.net/tt/172