Post List

2015년 1월 26일 월요일

item 6. C++의 most vexing parse 를 조심하자.



file을 읽어서 그것을 list<int>에 넣는 것을 구현할 때 아래와 같은 Code가 나올수 있다.

ifstream dataFile("ints.dat");
list<int> data(istream_iterator<int>(dataFile),
               istream_iterator<int>());


하지만 위 Code는 오류없이 Compile 된다. 하지만 제대로 동작하지 않는다. 왜그런지 천천히 분석해보자.

아래 Code들은 double을 매개 변수로 받아 int를 return 하는 함수 f의 선언이다.

int f(double d);
int f(double(d));
int f(double);

모두 동일하다. 변수를 둘러싼 괄호 ()는 무시되며, 선언에서는 매개변수 이름을 적지 않아도 된다.

조금 다른 형태의 함수선언을 3개 더 보자. 아래는 함수 포인터를 매개변수로 받아서 int를 return하는 함수 g의 선언이다.

int g(double(&pf)());
int g(double pf());
int g(double());

모두 동일하다. *를 표시하지 않아도 뒤에 괄호() 로 인하여 함수 포인터인지 알수 있으며, (C , C++에서 유효한 문법이다.) 변수명의 생략이 가능하다.

f와 g의 선언중 ()로 된것을 보면 조금 감이 올 것이다. 그럼 이제 왜 처음에 본 저 Code가 제대로 동작하지 않는지 살펴보자.

ifstream dataFile("ints.dat");
list<int> data(istream_iterator<int>(dataFile),
               istream_iterator<int>());

- 첫번째 매개변수는 dafaFile이란 이름을 가지고 있으며 그 Type은 istream_iterator<int>이다. 괄호 () 는 Compiler가 무시한다.
- 두번째 매개변수는 이름을 가지고 있지 않으며 istream_iterator<int>를 return하는 함수포인터이다.

황당하겠지만, 이것이 C++ Compile 규칙에 어긋남이 하나도 없다. 

class Widget { … };
Widget w();

위 문장에서 w라는 Widget 객체가 만들어졌을까 ?
그냥 w라는 이름을 가진 Widget 를 return하는 함수를 선언한 것이다.

그럼 다시 원래 문제로 돌아와서 보자. 어떻게 저 문제를 해결 할 수 있을까 ?

첫번째 해결방법으로는 괄호 하나면 쉽게 해결이 된다.

list<int> data((istream_iterator<int>(dataFile)),
                istream_iterator<int>());

첫번째 매개변수를 괄호() 로 묶었다. 이렇게 하면 첫번째 매개변수에서 제대로 istream_iterator<int>이 생성되어서 정상적으로 동작한다. 하지만 불행하게도 모든 Compiler에서 제대로 되는건 아니었다. 이건 Compiler의 비위를 맞추려 코드의 이식성을 떨어뜨리는 근시안적인 방법이다.

Compiler에 구애받지 않고 쓸 수 있는 더 좋은 방법이 있다.
두번째 해결방법으로는 익명(anonymous) 객체 선언을 쓰지말고 객체 이름을 만들어 주는 것이다.

ifstream dataFile("ints.dat");
istream_iterator<int> dataBegin(dataFile);
istream_iterator<int> dataEnd;
list<int> data(dataBegin, dataEnd);

통상적인 STL Programming Style은 아니지만 차라리 혼란을 주는 Code보다는 나을 것이다.

댓글 없음:

댓글 쓰기