Question

I'm unsure as to whether this code will not compile.

The example code I'm working with:

#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;
}

This is the error:

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’

I'm confused as to why it cannot substitute the function type of endl (ostream& (*)(ostream&)) for T, where it clearly is fine with doing it when you specify cout << endl;

I find it additionally puzzling that this fixes the problem [ edited ]

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

In case the question isn't clear, I'm asking why it could not deduce the template in the first place.

Was it helpful?

Solution

endl is a manipulator, i.e. it's an unresolved function type. There are several overloads, and the type deduction is unable to decide which one you want.

More specificly, here's what endl looks like (in 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'))); }

Updated So, the problem is, the compiler cannot deduce which instance of endl you would be passing (it's an unresolved overload). You might work around this by doing a static_cast<ostream&(*)(ostream&)>(endl) instead.

Of course, that's not convenient. Here's a simple fix: 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;
}

OTHER TIPS

The problem is that endl is a manipulator defined as a function template. Paragraph 27.7.1 of the C++11 Standard specifies its signature:

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

Moreover, per Paragraph 13.3.1 on overload resolution:

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.

Your operator << is a defined as a template, and the compiler needs to deduce the type of T. However, how can the compiler know which instantiation of endl you meant? How can it deduce the template arguments charT and traits? There is nothing else in your call to operator << from which it could be deduced.

You have two ways out of this problem. Either you cast the type of endl explicitly, to tell the compiler which overload shall be picked:

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

Or, as you did, you create an overload of operator << which accepts a function with that specific signature. Your compiler will now select it:

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

Inside this function definition there is no ambiguity as to what f is: its type is precisely defined. However, be careful here: this function doesn't likely do what you expect! In fact, it just keeps calling itself, generating an infinite recursion!

Therefore, this assertion:

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

Is incorrect: you're not calling the other method implementation, you keep calling the same function over and over again.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top