0

This will compile and run if you uncomment the first operator definition:

#include <iostream>

struct logger
{
    std::ostream &loggingStream;
    logger(std::ostream &ls) : loggingStream(ls) {}

};

/*
logger &operator<<(logger &l, std::ostream & (*manip)(std::ostream &)) {
    manip(l.loggingStream);
    return l;
}
*/

template<typename T>
logger &operator<<(logger &l, const T &t) {
    l.loggingStream << t;
    return l;
}

int main() {
    logger l(std::cout);

    l << "Hello" << std::endl;
    return 0;
}

With the comment in place:

error: no match for ‘operator<<’ (operand types are ‘logger’ and ‘<unresolved overloaded function type>’)

Why do I need to provide a non-template overload to handle endl?

spraff
  • 32,570
  • 22
  • 121
  • 229
  • 1/ see https://stackoverflow.com/q/1134388/136208 2/ writing a custom streambuf is the way the IO subsystem is designed for such kind of extension – AProgrammer Mar 06 '19 at 17:57

2 Answers2

1

Because, as a function template, std::endl is an overload set with regard to template argument deduction; and template argument deduction can't work on overload sets (unless it only contains one function of course).

To illustrate, consider:

template<class Function>
void functor(Function f)
{ f(0); }

void g(float) {}
void g(double) {}

functor(g);

There is no reason to favor one version of g over the other, and unless you explicitly specialize functor (functor<void(float)>(f) is fine), template argument deduction must fail.

This is also true if g is a template: http://coliru.stacked-crooked.com/a/8e27a45bbeedd979

YSC
  • 38,212
  • 9
  • 96
  • 149
1

std::endl is itself a template. When you have the first overload, its arguments can be deduced by matching to the function pointer. For that is one instance where TAD happens.

With just the operator<< template, what is there to deduce from? Both templates need their arguments deduced.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458