문제

Visual C ++ 컴파일러가 왜 잘못된 오버로드를 호출 하는가?

서식을 위해 버퍼를 정의하는 데 사용하는 타조의 서브 클래스가 있습니다. 때때로 나는 임시를 만들고 평소 << 연산자와 같이 문자열을 즉시 삽입하고 싶습니다.

M2Stream() << "the string";

불행히도, 프로그램은 연산자 << (Ostream, void *) 멤버 오버로드를 연산자에게 호출합니다.

나는 문제를 재현하는 자신의 m2stream 클래스를 정의하는 테스트로 아래 샘플을 썼습니다.

문제는 m2stream () 표현식이 임시를 생성하고 이로 인해 컴파일러가 무효 * 과부하를 선호하게된다는 것입니다. 하지만 왜? 이것은 내가 비 멤버 과부하 const m2stream에 대한 첫 번째 논쟁을한다면 모호함을 얻는다는 사실에 의해 부담이됩니다.

또 다른 이상한 점은 내가 원하는 const char * 오버로드를 호출한다는 것입니다.

const char *s = "char string variable";
M2Stream() << s;  

마치 문자 그대로 문자열이 const char * 변수와 다른 유형을 갖는 것처럼! 그것들은 같지 않아야합니까? 그리고 왜 컴파일러가 임시 문자열과 문자 그대로 문자열을 사용할 때 무효 * 오버로드를 호출하는 이유는 무엇입니까?

#include "stdafx.h"
#include <iostream>
using namespace std;


class M2Stream
{
public:
    M2Stream &operator<<(void *vp)
    {
        cout << "M2Stream bad operator<<(void *) called with " << (const char *) vp << endl;
        return *this;
    }
};

/* If I make first arg const M2Stream &os, I get
\tests\t_stream_insertion_op\t_stream_insertion_op.cpp(39) : error C2666: 'M2Stream::operator <<' : 2 overloads have similar conversions
        \tests\t_stream_insertion_op\t_stream_insertion_op.cpp(13): could be 'M2Stream &M2Stream::operator <<(void *)'
        \tests\t_stream_insertion_op\t_stream_insertion_op.cpp(20): or 'const M2Stream &operator <<(const M2Stream &,const char *)'
        while trying to match the argument list '(M2Stream, const char [45])'
        note: qualification adjustment (const/volatile) may be causing the ambiguity
*/
const M2Stream & operator<<(M2Stream &os, const char *val)
{
    cout << "M2Stream good operator<<(const char *) called with " << val << endl;
    return os;
}


int main(int argc, char argv[])
{
    // This line calls void * overload, outputs: M2Stream bad operator<<(void *) called with literal char string on constructed temporary
    M2Stream() << "literal char string on constructed temporary";

    const char *s = "char string variable";

    // This line calls the const char * overload, and outputs: M2Stream good operator<<(const char *) called with char string variable
    M2Stream() << s;  

    // This line calls the const char * overload, and outputs: M2Stream good operator<<(const char *) called with literal char string on prebuilt object
    M2Stream m;
    m << "literal char string on prebuilt object";
    return 0;
}

산출:

M2Stream bad operator<<(void *) called with literal char string on constructed temporary
M2Stream good operator<<(const char *) called with char string variable
M2Stream good operator<<(const char *) called with literal char string on prebuilt object
도움이 되었습니까?

해결책

컴파일러가 올바른 일을하고 있습니다. Stream() << "hello"; 사용해야합니다 operator<< 멤버 함수로 정의됩니다. 임시 스트림 객체는 정점이 아닌 참조에 바인딩 될 수 없지만 Const 참조에만 적용되기 때문에 처리하는 비회원 연산자 char const* 선택되지 않습니다.

그리고 연산자를 변경할 때 알 수 있듯이 그런 식으로 설계되었습니다. 컴파일러는 사용할 수있는 운영자 중 어느 것이 사용될 지 결정할 수 없기 때문에 모호성이 있습니다. 그들 모두는 비 멤버를 거부하여 설계 되었기 때문에 operator<< 임시를 염두에두고 있습니다.

그렇다면 문자열 문자는 다른 유형을 가지고 있습니다. char const*. 문자열 문자는 const 문자의 배열입니다. 그러나 그것은 당신의 경우에는 중요하지 않을 것입니다. 나는 어떤 오버로드가 있는지 모르겠습니다 operator<< MSVC ++ 추가. 유효한 프로그램의 동작에 영향을 미치지 않는 한 오버로드를 추가 할 수 있습니다.

이유를 위해 M2Stream() << s; 첫 번째 매개 변수가 원인이 아닌 참조 인 경우에도 작동합니다 ... 음, MSVC ++는 비정규 참조가 임시에 바인딩 할 수있는 확장 기능을 가지고 있습니다. 경고 레벨을 레벨 4에 넣고 그에 대한 경고를보십시오 ( "비표준 확장자 사용 ..."와 같은 것).

이제 멤버 연산자가 있기 때문에 void const*, a char const* 이로 변환 할 수 있습니다. 해당 연산자가 선택되고 주소는 출력됩니다. void const* 과부하는 용입니다.

나는 당신의 코드에서 당신이 실제로 가지고 있음을 보았습니다. void* 과부하가 아닙니다 void const* 초과 적재. 글쎄, 문자열 문자가 변환 할 수 있습니다 char*, 문자열 문자의 유형은 char const[N] (n은 당신이 넣은 캐릭터의 양이기도합니다). 그러나 그 전환은 더 이상 사용되지 않습니다. 문자열 문자가 void*. MSVC ++ 컴파일러의 또 다른 확장자 인 것 같습니다. 그러나 그것은 문자열 문자가 왜와 다르게 취급되는지 설명 할 것입니다. char const* 바늘. 이것이 표준의 말입니다.

넓은 문자열 문자가 아닌 문자열 리터럴 (2.13.4)은 "Pointer to Char"유형의 rvalue로 변환 될 수 있습니다. 넓은 문자열 문자는 "WCHAR_T에 대한 포인터"유형의 rvalue로 변환 될 수 있습니다. 두 경우 모두 결과는 배열의 첫 번째 요소에 대한 포인터입니다. 이 변환은 명백한 적절한 포인터 대상 유형이있을 때만 고려되며 LValue에서 rvalue로 전환 해야하는 일반적인 필요가없는 경우가 아닙니다. [참고 :이 변환은 더 이상 사용되지 않습니다. 부록 D 참조

다른 팁

첫 번째 문제는 이상하고 까다로운 C ++ 언어 규칙으로 인해 발생합니다.

  1. 생성자에 대한 호출에 의해 생성 된 임시는 다음과 같습니다. rvalue.
  2. rvalue는 비 정중 참조에 바인딩되지 않을 수 있습니다.
  3. 그러나 rvalue 객체는 비 초가의 메소드를 호출 할 수 있습니다.

일어나고있는 일은 그 것입니다 ostream& operator<<(ostream&, const char*), 비회원 함수는 바인딩을 시도합니다 M2Stream 일시적으로 당신은 비 초가의 참조로 만들지 만 실패합니다 (규칙 #2). 하지만 ostream& ostream::operator<<(void*) 멤버 함수이므로 바인딩 할 수 있습니다. 없을 때 const char* 함수는 최고의 과부하로 선택됩니다.

iostreams 도서관의 디자이너가 왜 만들기로 결정했는지 잘 모르겠습니다. operator<<() ~을 위한 void* 방법이지만 그렇지 않습니다 operator<<() ~을 위한 const char*, 그러나 그것이 그것이 어떻게 이루어 지므로, 우리는 이러한 이상한 불일치를 다루고 있습니다.

두 번째 문제가 왜 발생하는지 잘 모르겠습니다. 다른 컴파일러에서 동일한 동작을 얻습니까? 컴파일러 또는 C ++ 표준 라이브러리 버그 일 가능성이 있지만 최후의 수단의 변명으로 남겨 두십시오. 최소한 정기적으로 동작을 복제 할 수 있는지 확인하십시오. ostream 첫 번째.

문제는 임시 스트림 객체를 사용하고 있다는 것입니다. 코드를 다음으로 변경하면 작동합니다.

M2Stream ms;
ms << "the string";

기본적으로 컴파일러는 임시를 비 Const 참조에 바인딩하는 것을 거부합니다.

"const char *"객체가있을 때 왜 묶는 지에 대한 두 번째 요점에 대해서는 VC 컴파일러의 버그라고 생각합니다. 그러나 당신이 문자열 문자가있을 때는 'void *'로 전환하고 'const char *'로 전환됩니다. 'const char *'객체가 있으면 두 번째 인수에 전환이 필요하지 않습니다. 이는 VC의 비표준 동작이 비 const ref bind를 허용하는 트리거 일 수 있습니다.

나는 8.5.3/5가 이것을 다루는 표준의 섹션이라고 생각합니다.

코드가 컴파일 해야하는지 잘 모르겠습니다. 제 생각에는:

M2Stream & operator<<( void *vp )

해야한다:

M2Stream & operator<<( const void *vp )

사실, 코드를 더 살펴보면 모든 문제가 Const로 달려 있다고 생각합니다. 다음 코드는 예상대로 작동합니다.

#include <iostream>
using namespace std;


class M2Stream
{
};

const M2Stream & operator<<( const M2Stream &os, const char *val)
{
    cout << "M2Stream good operator<<(const char *) called with " << val << endl;
    return os;
}


int main(int argc, char argv[])
{
    M2Stream() << "literal char string on constructed temporary";

    const char *s = "char string variable";

    // This line calls the const char * overload, and outputs: M2Stream good operator<<(const char *) called with char string variable
    M2Stream() << s;  

    // This line calls the const char * overload, and outputs: M2Stream good operator<<(const char *) called with literal char string on prebuilt object
    M2Stream m;
    m << "literal char string on prebuilt object";
    return 0;
}

다음과 같은 과부하를 사용할 수 있습니다.

template <int N>
M2Stream & operator<<(M2Stream & m, char const (& param)[N])
{
     // output param
     return m;
}

추가 보너스로, 당신은 이제 n이 배열의 길이임을 알고 있습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top