[ [Mistakable Codes - #4] 연산자 오로딩 시 주의 사항. operator<< operator overloading, error C2666: ‘<<‘ : 13 overloads have similar conversions

8 Comments

근래 (약 1주일 동안) 이런저런 결정할 일들과 알아볼 일이 있어서 블로깅을 거의 못했습니다.
아무튼, 오늘은 간과하기 쉬운 실수에 대해서 포스팅을 합니다.
(이와 관련되어서는 언제 한 번 포스팅하겠습니다. ^^;; 지극히 사적인 부분이기는 하나)

다음 코드는 간단히 CTest라는 class를 만들어서 std::ostringstream::operator<<를 overloading했습니다.
즉, CTest를 operator<<가 인자로 받을 때 int 멤버 변수인 m_nNum을 operator<<에 넘겨주는 코드입니다.

하지만 위 코드를 컴파일 하면 아래와 같은 에러 메시지를 만나게 됩니다.

error C2666: ‘<<’ : 13 overloads have similar conversions

말그대로 operator<<를 overloading한 것중 어느 것을 선택해야 할지 모르겠다는 것입니다.
즉, (1)에서 컴파일러는 13개의 overloading 된 함수 후보 중 어떤 것을 사용할지 모르겠다는 것입니다.

해결책 – 1
간단하게 컴파일러에게 매개 변수로 int를 받으면 CTest의 생성자를 써라고 명시적으로 지정해주는 것입니다. 즉, explicit 키워드를 사용하는 것입니다.
※ CTest의 생성자를 int에 대해서 명시적으로 사용하게 정의하는 것으로 볼 수 있겠죠.


해결책 -2

하지만 explicit를 사용하면 다음과 같이 컴파일러가 알아서 디폴트 생성자를 불러주는 호화스러운 기능은 사용할 수 없습니다.

이런 부분을 고려해서, 아래와 같이 디폴트 생성자를 따로 만들어주고 CTest(int)의 디폴트 값을 제거할 수도 있습니다.
gimmesilver 님이 지적해주신 것 처럼 아래 코드 처럼 디폴트 생성자를 작성한다고 해서 A a =1;과 같은 코드가 가능해지는 것은 아닙니다.
디폴트 생성자가 필요한 다른 부분 (즉, CTest(int a =0); 으로 하면 디폴트 생성자가 굳이 없어도 되지만)을 위한 것이라고 보시면 됩니다.)

Ref: comp.lang.c++ Is it VC++ or I am going crazy???

^^ 그리고 gimmesilver님의 마지막 지적 처럼 위 코드의 옳고 그름의 가치를 떠나 Legacy나 가져다 쓰고 있는 오픈 소스에서 발생했을 경우 때문에 포스팅을 합니다.

  • hyunghunny

    간만에 alones 님의 글을 읽으니 기분이 좋습니다. (SM5 광고 풍?!)
    거의 매일 뵙는데도 불구하고 대화는 온라인에서 더 많이 하는 듯… -_-;;

    행복한 하루 되시길!

  • http://agbird.egloos.com gimmesilver

    좀더 정확히 말하자면 에러는 (1)이 아니라 소스의 20번째 줄인 << 연산자 재정의 부분에서 발생합니다. << 연산자 재정의 시 int 값을 인자로 넘겨주지만 이 int값은 Test(int) 생성자 때문에 Test 객체로 형변환이 가능하게되고 따라서 재정의한 연산자 프로토타입과 또 일치하게 되어 컴파일러입장에서는 <<(int) 와 <<(const CTest& ) 중 어떤 함수를 호출해야 할지 모호해지는 것이죠…
    그리고 해결책2 에서 디폴트 생성자를 추가해주더라도 CTest a = 1; 과 같은 문장은 여전히 컴파일되지 않습니다.
    (그리고 이런 코드는 바람직하지 않은 코드이기 때문에 굳이 해결책을 찾을 필요도 없을 듯 싶습니다.)

  • http://alones.kr/blog alones

    안녕하세요~

    말씀하신 것 처럼 실제 컴파일러가 에러를 토해내는 장소는 말씀하신 부분이 맞습니다.
    하지만 그 에러를 유발 (즉, (1) 부분이 없다면 에러는 없을 겁니다)하는 코드가 (1)이라는 뜻으로 썼습니다.

    그리고 마지막에 지적하신 부분은 제가 잘 못 썼군요.
    포스트 내용 상 CTest a = 1; 과 같은 기능을 사용하기 위해서라기 보다는 단지, 디폴트 생성자를 사용해야하는 경우 (operator=을 이용한 대입 등에서)라고 해야 맞을 겁니다.

    그리고 코드가 바람직하다 하지 않다라는 부분에서는…
    ^^ 레가시 코드나 다른쪽 코드의 유지 보수를 위해서 즉, 다른 사람이 작성한 코드를 사용해야할 경우를 위해서로 보시면 좋겠습니다.

    실제로 제 경우는 오픈 소스를 계속 사용하다가 문제가 생겨서 해결했던 것을 경험으로 포스팅을 한 것입니다.

    좋은 의견 감사합니다. ^^

  • http://alones.kr/blog alones

    아 그리고 주고 받은 내용을 포스트에 반영했습니다. 좋은 하루되세요~

  • http://blog.naver.com/moto99 suban

    퍼갈게요^^

  • http://agbird.egloos.com gimmesilver

    사용하신 컴파일러가 어떤 것인지요? 제가 테스트한 mingw에서는 (1)과 상관없이 컴파일 에러가 발생합니다.
    그런데 혹시나 컴파일러마다 다른가 해서 리눅스 g++에서 테스트를 했더니 여기서는 (1)과는 상관없이 << 재정의 연산자를 클래스 외부에 정의하면 에러가 나고 (위에 제시하신 예제처럼) CTest 클래스의 friend로 내부에서 정의하면 에러가 발생하지 않는군요…
    아마도 클래스나 함수를 바인딩하는 시점이 컴파일러마다 달라서 그런 듯 싶습니다.
    어쨌든 해당 함수를 호출하지 않는다고 해서 함수 내에 있는 에러를 컴파일러가 인식하지 않는다는 것은 좀 이상합니다. (뭐 함수 템플릿이라면 인스턴스화가 이뤄져야 컴파일이 되니 그런 경우가 있지만…)

  • http://alones.kr/blog alones

    관심을 가져주시고 계속 feedback을 주셔서 감사합니다. ^^

    제가 답변이 좀 늦었습니다.

    CTest를 위 포스트처럼 같은 cpp나 또는 header 파일에 두었을 때는 (1)의 여부에 따라 컴파일 에러가 나지않습니다만…

    현재 작업하고 있는 코드는 (회사내의 코드라서 밖으로 가져올 수가 없습니다) CTest와 같은 녀석이 include 관계 상 몇 단계를 거쳐 상위에 있습니다. 그 코드에서는 (1)의 사용 여부에 따라 현재 컴파일 에러 발생이 달라지고 있습니다.

    VC6.0을 사용하고 있는데요.. 전처리를 한 중간 파일을 살펴봐도 문제는 없어 보입니다.

    현재 작업 코드의 다른 부분에 문제가 있을 수도 있구요.. 아무튼 시간을 내서 코드를 좀 뜯어 볼려고합니다.

    좀 정리를 하면
    1. 말씀하신 것 처럼 일반적으로 (1)의 사용 여부에 따라 컴파일 에러가 발생하는 것은 아닙니다.
    2. 현재 제가 작업하고 있는 코드에서는 (1)을 사용하면 컴파일 에러가 발생하니다. (explicit를 처리하지 않았겠죠)

    아무튼 다른 일 때문에 이 문제 분석을 잘 못하고 있는데.. 결과가 나오면 포스팅이나 댓글쓰겠습니다.

    감사합니다. :)

  • 유주형

    좋은 글 감사합니다.
    저도 같은 문제로 고민하고 있었는데..해결점을 찾았네요 ^^