2013-02-23 3 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; 
} 

Это ошибка:

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’ 

Я запутался, почему он не может заменить тип функции из endl (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 является манипулятором, то есть неразрешенный тип функции. Существует несколько перегрузок, и вычет типа не может решить, какой из них вы хотите.

Более специфически, вот что 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 Я предоставил простой пример того, как обслуживать манипулятор 'endl': http://liveworkspace.org/code/2F2VHe$1 – sehe

+0

Не могли бы вы приукрасить различия между вашим кодом и код, который у меня был выше, и причины различий. Например, мое текущее «решение» делает то же самое с явным определением перегрузки для манипулятора, но вы могли бы объяснить использование «std :: forward». Также вы могли бы объяснить причины, почему компилятор не может выбрать перегруз без этого явного определения. –

1

Проблема заключается в том, что endl является манипулятором определяется как шаблон в функции. Пункт 27.7.1 из C++ 11 Стандарт определяет свою подпись:

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

Кроме того, в соответствии с пунктом 13.3.1 на перегруженных:

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 << является определяется как шаблон, и компилятор должен вывести тип T. Однако как компилятор может узнать, какой экземпляр endl вы имели в виду? Как он может вывести аргументы шаблона charT и traits? Нет ничего другого в вашем обращении к operator <<, из которого это можно было бы вывести.

У вас есть два пути выхода из этой проблемы.Либо вы приводите тип endl явно, чтобы сообщить компилятору, какие перегрузки должны быть выбраны:

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

Или, как вы это сделали, вы создаете перегрузку operator <<, которая принимает функцию с этой конкретной подписи. Ваш компилятор теперь будет выбрать его:

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

Внутри этого определения функции не существует неоднозначность относительно того, что f является: его тип точно определен. Однако будьте осторожны: эта функция вряд ли сделает то, что вы ожидаете! Фактически, он просто продолжает называть себя, генерируя бесконечную рекурсию !

Поэтому это утверждение:

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

Является неправильно: вы не вызывая другую реализацию методы, вы продолжаете называть ту же функцию, снова и снова.

+0

стрелять, хорошо поймать, я, вероятно, должен был запустить мой код, а не просто проверять его компиляцию ... –