[C++] Most vexing parse
https://en.wikipedia.org/wiki/Most_vexing_parse
C++에서 꽤나 고전적인 문법 모호성(ambiguity) 문제이다. 컴파일러에게는 모호하지만 사람 입장에선 모호하다고 생각하기 어려운 비직관적인(counter-intuitive) 모호성이라서, 막상 맞닥뜨리면 원인을 알아채기 어려우므로 미리 알아두면 좋다. 나는 몰랐어서 한참을 쳐다보고 해결했다
바로 코드를 보자.
void someFunc(string_view text)
{
// text를 먼저 string 타입으로 변환한 후 istringstream 생성자의 인자로 넣는다
std::istringstream ss(std::string(text));
}
string_view text;
평범한 istringstream
타입 객체 선언 코드로 보이지만, 놀랍게도 컴파일러는 std::istringstream ss(std::string(text));
를 함수의 선언(declaration)
으로 인식한다. 원인은 다음과 같다.
- 함수 내에서 함수를 ‘선언(declaration)‘하는 것이 가능하다 (참고: definition은 불가)
- 컴파일러는
타입명(파라미터명)
과 같은 꼴의 파라미터의 선언 또한 인정한다. 예를 들어,void someFunc(int(my_num))
은 가능한 함수 선언이다.
위와 같은 이유로 인해, 컴파일러는 ‘객체 생성 구문(object parameter)‘과 ‘함수의 선언(declaration of function)‘을 구분하지 못하게 된다.
위 예시 코드에서는 std:istringstream
의 생성자에 또 다른 타입의 생성자를 중첩해 호출하여 모호성이 생겼으나, 괄호 문자 ()
가 모호성의 직접적인 원인이기 때문에 C-style 캐스팅 또한 원인이 될 수 있다. (다른 예시 코드는 wikipedia를 참고하길 바란다.)
해결 방법
타입명(파라미터명)
과 같은 꼴로 작성하는 것을 피한다.- Uniform Initilization (통일된 초기화)를 사용한다. (C++11)
방법1
// 방법1 적용
// 여러 줄로 나눠 작성하여 `타입명(파라미터명)`꼴을 피함
std::string str(text);
std::istringstream ss(str);
방법2
// 방법2 적용
// Uniform Initilization 사용 (C++11)
std::istringstream ss{std::string(text)};
std::istringstream ss{std::string{text}}; // 둘다 가능