2013-02-23 16 views
4

이 코드가 컴파일되지 않을지 확실하지 않습니다.스트림 조작기에 대한 템플릿 유형 공제

내가 함께 일하고 있어요 예제 코드 :

#include <iostream> 
using std::cout; 
using std::endl; 

class Foo { 
    public: 
     template<typename T> 
     Foo& operator<<(const T& t) { 
      cout << t; 
      return *this; 
     } 
}; 

int main() { 
    Foo foo; 
    foo << "Hello World"; // perfectly fine 
    foo << endl; // shit hits the fan 

    return 0; 
} 

이 오류입니다 : 나는 endl의 기능 유형을 대체 할 수없는 이유에 혼란 스러워요

test.cpp:19:12: error: no match for ‘operator<<’ in ‘foo << std::endl’ 
test.cpp:19:12: note: candidates are: 
test.cpp:10:14: note: template<class T> Foo& Foo::operator<<(const T&) 
test.cpp:10:14: note: template argument deduction/substitution failed: 
test.cpp:19:12: note: couldn't deduce template parameter ‘T’ 

(ostream& (*)(ostream&)) T에 대해 지정한 경우 분명히 괜찮 으면 cout << endl;

찾을 수 있습니다. addi 전통적으로이가 처음에 템플릿을 추론 할 수없는 이유를 질문이 명확하지, 내가 부탁 해요한다 [편집] 문제 경우

Foo& operator<<(ostream& (*f)(ostream&)) { 
    cout << f; 
    return *this; 
} 

해결되는 수수께끼.

+0

달성하려는 목표는 무엇입니까? 'std :: basic_ostream '또는 streambuf 구현에서 파생되지 않은 특별한 이유가 있습니까? – sehe

+0

마지막 질문 : ** 여러 오버로드 **가 적용 가능하므로 템플릿을 추론 할 수 없었습니다. 따라서 함수 참조 사례가 모호했습니다. – sehe

답변

3

endl은 조작자, 즉 미해결 함수 유형입니다. 과부하가 여러 개 있고 유형 공제로 어느 것을 원하는지 결정할 수 없습니다.

더 specificly, 여기 endl은 (GNU의 libc의 ++)의 모습입니다 :

/** 
* @brief Write a newline and flush the stream. 
* 
* This manipulator is often mistakenly used when a simple newline is 
* desired, leading to poor buffering performance. See 
* http://gcc.gnu.org/onlinedocs/libstdc++/manual/bk01pt11ch25s02.html 
* for more on this subject. 
*/ 
template<typename _CharT, typename _Traits> 
    inline basic_ostream<_CharT, _Traits>& 
    endl(basic_ostream<_CharT, _Traits>& __os) 
    { return flush(__os.put(__os.widen('\n'))); } 

업데이트 그래서, 문제가, 컴파일러는 하는 예를 당신이 통과 될endl의를 추론 할 수 없습니다 (해결되지 않은 과부하). 대신 static_cast<ostream&(*)(ostream&)>(endl)을 수행하면이 문제를 해결할 수 있습니다.

물론 편리하지 않습니다. 다음은 간단한 수정입니다 : http://liveworkspace.org/code/2F2VHe$1

#include <iostream> 
using std::cout; 
using std::endl; 

class Foo : public std::ostream 
{ 
    public: 
     template<typename T> 
     Foo& operator<<(T&& t) { 
      cout << std::forward<T>(t); 
      return *this; 
     } 

     typedef std::ostream& (manip)(std::ostream&); 

     Foo& operator<<(manip& m) { 
      cout << m; 
      return *this; 
     } 
}; 

int main() { 
    Foo foo; 
    foo << "Hello World"; // perfectly fine 
    foo << endl; // everything is fine 

    return 0; 
} 
+0

더 설명해 주시겠습니까? 올바른 오버 헤드를 결정하기 위해 실제로 다른 오버로드 대체를 시도하지 않았기 때문입니다. 아니면 컴파일러에서 너무 많이 기대하고있다. –

+0

@AnthonySottile 나는 manipulator'endl'에 대한 간단한 샘플을 제공했다 : http://liveworkspace.org/code/2F2VHe$1 – sehe

+0

코드와 위의 코드와 차이점에 대한 이유. 예를 들어, 현재의 "솔루션"은 명시 적으로 매니퓰레이터의 오버로드를 정의하는 것과 동일한 작업을 수행하지만,'std :: forward'의 사용법을 설명 할 수는 있습니다. 또한 컴파일러가 명시 적 정의없이 오버로드를 선택할 수없는 이유에 대한 이유를 설명 할 수 있습니다. –

1

문제는 endl기능 템플릿로 정의 된 조작기는 것입니다. 단락의 27.7.1는 C++ 11 표준은 서명을 지정

In each case where a candidate is a function template, candidate function template specializations are generated using template argument deduction (14.8.3, 14.8.2). Those candidates are then handled as candidate functions in the usual way.

귀하의 operator << A A 템플릿으로 정의되며, : 오버로드 확인에

template <class charT, class traits> 
basic_ostream<charT,traits>& endl(basic_ostream<charT,traits>& os); 
template <class charT, class traits> 

또한, 단락 13.3.1 당을 컴파일러는 T의 유형을 추론해야합니다. 그러나 컴파일러는 endl의 인스턴스화를 어떻게 알 수 있습니까? 템플릿 인수 인 charTtraits을 어떻게 추론 할 수 있습니까? 당신의 전화 번호 중 operator <<에 대한 다른 정보는 추론 될 수 없습니다.

두 가지 방법으로 문제가 발생했습니다.

foo << (std::ostream& (*)(std::ostream&))endl; 

또는, 당신이 그랬던 것처럼, 당신은 특정 서명 기능을 허용 operator <<의 과부하를 만들 : 어느 당신이 포착해야한다 과부하 컴파일러에게 명시 적으로 endl의 유형을 캐스팅. 귀하의 컴파일러는 지금 선택합니다

Foo& operator<<(ostream& (*f)(ostream&)) 
{ 
    return *this << f; 
} 

이 함수 정의 내부 모호성은 f가 무엇인지에 관해서는 없다 : 그 유형은 정확하게 정의된다. 그러나 여기서주의하십시오.이 함수는 예상 한대로 작동하지 않습니다! 실제로, 그것은 단지 무한 재귀 호출을 생성합니다.

따라서이 주장 :

[...] note I'm actually calling my other method implementation:

잘못된인가 : 당신은 당신이 계속해서 또 다시 같은 함수를 호출 계속 다른 방법의 구현을 호출 아닙니다.

+0

슛, 잘 잡으십시오. 아마도 컴파일을 점검하는 대신 내 코드를 실행해야했을 것입니다 ... –