2008.10.16 08:06 책 정리/Exceptional C++
다음 문제의 미묘한 문제를 지적해보자.

이름 은폐(name hiding)는 무엇인가? 파생된 클래스에서 기반 클래스 이름의 가시성에 어떻게 영향을 주는지 밝혀라

② 다음 예제가 정확하게 컴파일 되는가? 가능한 좋은 답변을 달아보고, 의심 되는 부분을 고립시킨 후 설명해 봐라.

분석


가상 함수가 아니면 이름을 절대 가리지 말라고, Effective C++ 에서 설명하고 있었고, 역시 이 부분에 대해서 자세히 언급하고 있으므로, 생략한다.


 우선 컴파일 해보자. 어떤가? 컴파일이 되는가? 아마도 되지 않을 것이다.  예제코드를 잘 보면, #include <numeric> 포함시켜 수학 알고리즘 std::accumlate() 를 사용 하고 있다. 그러므로 std::accumlate()의 사용법 부터 알아 보자.


다시 본론으로 돌아와서 이번 항목에서 사용 하는 제 첫번째 방법(22 라인) 의 소스 코드를 한번 보도록 하자.


 자세히 보면 8 라인에 실질적인 연산 작업이 들어 가는 것을 확인 할 수 있을 것이다. "어? 이상하다. 분명 전역 함수로 operator+(int, N::C) 을 제공했는데, 왜 에러가 나지?" 라고 생각 할 것 이다.  그러면 이제 에러 코드를 봐보자.

해당 에러코드는 MSVC2005 에서 나온 것들이다.
1.
1>c:\program files\microsoft visual studio 8\vc\include\numeric(23) : error C2784: 'std::reverse_iterator<_RanIt> std::operator +(_Diff,const std::reverse_iterator<_RanIt> &)' : 'const std::reverse_iterator<_RanIt> &'의 템플릿 인수를 'N::C'에서 추론할 수 없습니다.
이건 뭐지? 나는 const std::reserv_iterator를 사용 한 적이 없는데?


2.
1>        c:\program files\microsoft visual studio 8\vc\include\xutility(1809) : 'std::operator +' 선언을 참조하십시오.
으잉? 나는 xutility의 std::operator+ 를 사용하지 않고 내가 정의한 전역 operator+(int, N::C)를 사용 하려고 했는데 왜 이녀석을 쓰고 난리야?


3.
1>        c:\program files\microsoft visual studio 8\vc\include\numeric(31) : 컴파일 중인 함수 템플릿 인스턴스화 '_Ty std::_Accumulate<N::C*,_Ty>(_InIt,_InIt,_Ty)'에 대한 참조를 확인하십시오.
1>        with
1>        [
1>            _Ty=int,
1>            _InIt=N::C *
1>        ]
이건 정확하게 호출이 되어졌고...


4.
1>        d:\projects\test\test\main.cpp(22) : 컴파일 중인 함수 템플릿 인스턴스화 '_Ty std::accumulate<N::C*,int>(_InIt,_InIt,_Ty)'에 대한 참조를 확인하십시오.
1>        with
1>        [
1>            _Ty=int,
1>            _InIt=N::C *
1>        ]
이것도 정확하게 호출 되어 졌는데...

5
1>c:\program files\microsoft visual studio 8\vc\include\numeric(23) : error C2677: 이항 '+' : 'N::C' 형식을 사용하는 전역 연산자가 없거나 허용되는 변환이 없습니다.
음? N::C 형식을 사용하는 전역 연산자가 있는데, 왜 없거나 허용되는 변환이 없다고 하지?

이 5 분류의 에러코드 중  3번과 4번의 확인으로 accumulate가 정확하게 템플릿화 된것은 확인 되었지만 1번과 2번의 에러코드는 도통 무슨 말인지 모르겠다.

마지막 5번 에러코드가 가장 신경이 쓰이는데 "N::C' 형식을 사용하는 전역 연산자가 없거나 허용되는 변환이 없습니다." 이 부분이 미심쩍다.

쉽게 풀면, "내가 정의한 operator+(int, N::C)가 보이지 않는다"는 말이다.


그렇다면 왜 보이지 않을까?
1번과 2번의 에러코드를 보면, 전혀 쌩뚱맞는 const std::reserve_iterator을 사용 되며, xutility 의 operator+를 사용되서 N::C 형으로 형 변환 할 수 없다는 결론에 도달한다.

이것은 .. 클래스의 이름 은폐(name hiding)와 동일한 이름 검색을 사용 함으로써, 컴파일러의 시야를 가린 상태랑 같은 유형의 에러코드 이다.

.. 결론이 나왔다. 네임스페이스에서도 "이름 은폐"가 있다는 것이다.

최초 accumulate에 값을 전달 하면 템플릿에 의하여 accumualte<N::C*, int> 의 함수가 만들어 지고, operator+를 찾기 위해서 제 1순위의 std:: 영역에서 이름을 찾을 것이다. 또한 동등한 위치에서 N:: 영역에서 operator+ 를 찾을 것이다.

 이때 operator+가 전혀 정의 되어 있지 않다면 Koenig 검색에 의하여 :: 영역(전역)에서 찾으려고 하겠지만 이미 operator+가 정의 되어 있다면, std:: N:: 중에서 컴파일러가 가장 합당한 operator+와 연결을 시키고, 그 과정 중에 컴파일 에러를 벹어 낸 것이다.


operator+(int, N::C)에서 볼수 있듯이 N::C를 언급하고 있다면, N::C는 operator+에 의존 되므로, N을 제공해야만 한다.( 위 문제의 경우에선 ... )


총평

클래스 뿐만 아닌 네임스페이스에서도 이름 은폐(name hiding)가 있다는 사실을 알게 되었고, 템플릿 에러는 정말 복잡하는 사실을 세삼 깨달게 되었다.  네임스페이스는 좀 더 현명하게 사용 해야겠으며(언급 되었다면 제공해야 되는지 생각해야 되는 부분), Exceptional C++ 은 정말 최고의 책이라는 사실 또한 알게 된다.

클래스의 이름 은폐(name hiding)는 Effective C++ 에서 2 항목에 걸쳐 설명하므로 생각을 하였지만, 뒤에 나오는 네임스페이스 이름 은폐(name hiding)와 동일한 구조이므로, 충분히 이해 할 수 있으리라 본다.
posted by 농사를 짓는 게임 프로그래머 최익필

댓글을 달아 주세요