glades
glades

Reputation: 4960

Why isn't the compiler able to deduce type of std::endl for my operator<<(T&&)?

I'm using perfect forwarding to pipe a type through to a associated JsonRecord object. However, compiler is unable to deduce the type of std::endl when I want to pick it up as a token to forward to std::ostringstream. Here's the exact code that's failing:

Live Demo

#include <string>
#include <iostream>
#include <sstream>
#include <memory>


struct JsonRecord
{
    JsonRecord() = delete;
    JsonRecord(JsonRecord&&) = default;
    ~JsonRecord()
    {
        std::cout << _ostr.str() << std::endl;
    };

    template <typename T>
    auto operator<<(T&& entry) -> JsonRecord&
    {
        _ostr << std::forward<T>(entry);
        return *this;
    }

    std::ostringstream _ostr;
};


struct Log
{
    template <typename T>
    auto operator<<(T&& entry) -> JsonRecord
    {
        auto record = JsonRecord{};
        record << std::forward<T>(entry);
        return record;
    };
};

int main()
{
    Log mylog;
    // This works
    mylog << "hello";
    // Then why doesn't this work?
    mylog << "hello" << std::endl;
}

With this error message:

    <source>: In function 'int main()':
<source>:44:22: error: no match for 'operator<<' (operand types are 'JsonRecord' and '<unresolved overloaded function type>')
   44 |     mylog << "hello" << std::endl;
      |     ~~~~~~~~~~~~~~~~~^~~~~~~~~~~~
<source>:17:10: note: candidate: 'template<class T> JsonRecord& JsonRecord::operator<<(T&&)'
   17 |     auto operator<<(T&& entry) -> JsonRecord&
      |          ^~~~~~~~
<source>:17:10: note:   template argument deduction/substitution failed:
<source>:44:30: note:   couldn't deduce template parameter 'T'
   44 |     mylog << "hello" << std::endl;
      |                              ^~~~
In file included from /opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/bits/basic_string.h:48,
                 from /opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/string:55,
                 from <source>:1:
/opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/string_view:667:5: note: candidate: 'template<class _CharT, class _Traits> std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&, std::basic_string_view<_CharT, _Traits>)'
  667 |     operator<<(basic_ostream<_CharT, _Traits>& __os,
      |     ^~~~~~~~
/opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/string_view:667:5: note:   template argument deduction/substitution failed:
<source>:44:30: note:   'JsonRecord' is not derived from 'std::basic_ostream<_CharT, _Traits>'
   44 |     mylog << "hello" << std::endl;
      |                              ^~~~
In file included from /opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/string:55,
                 from <source>:1:
/opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/bits/basic_string.h:6531:5: note: candidate: 'template<class _CharT, class _Traits, class _Alloc> std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&, const std::__cxx11::basic_string<_CharT, _Traits, _Allocator>&)'
 6531 |     operator<<(basic_ostream<_CharT, _Traits>& __os,
      |     ^~~~~~~~
/opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/bits/basic_string.h:6531:5: note:   template argument deduction/substitution failed:
<source>:44:30: note:   'JsonRecord' is not derived from 'std::basic_ostream<_CharT, _Traits>'
   44 |     mylog << "hello" << std::endl;
      |                              ^~~~
In file included from /opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/bits/ios_base.h:46,
                 from /opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/ios:42,
                 from /opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/ostream:38,
                 from /opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/iostream:39,
                 from <source>:2:
/opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/system_error:279:5: note: candidate: 'template<class _CharT, class _Traits> std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&, const std::error_code&)'
  279 |     operator<<(basic_ostream<_CharT, _Traits>& __os, const error_code& __e)
      |     ^~~~~~~~
/opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/system_error:279:5: note:   template argument deduction/substitution failed:
<source>:44:30: note:   'JsonRecord' is not derived from 'std::basic_ostream<_CharT, _Traits>'
   44 |     mylog << "hello" << std::endl;
      |                              ^~~~
In file included from /opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/iostream:39,
                 from <source>:2:
/opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/ostream:513:5: note: candidate: 'template<class _CharT, class _Traits> std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&, _CharT)'
  513 |     operator<<(basic_ostream<_CharT, _Traits>& __out, _CharT __c)
      |     ^~~~~~~~
/opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/ostream:513:5: note:   template argument deduction/substitution failed:
<source>:44:30: note:   'JsonRecord' is not derived from 'std::basic_ostream<_CharT, _Traits>'
   44 |     mylog << "hello" << std::endl;
      |                              ^~~~
In file included from /opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/iostream:39,
                 from <source>:2:
/opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/ostream:518:5: note: candidate: 'template<class _CharT, class _Traits> std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&, char)'
  518 |     operator<<(basic_ostream<_CharT, _Traits>& __out, char __c)
      |     ^~~~~~~~
/opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/ostream:518:5: note:   template argument deduction/substitution failed:
<source>:44:30: note:   'JsonRecord' is not derived from 'std::basic_ostream<_CharT, _Traits>'
   44 |     mylog << "hello" << std::endl;
      |                              ^~~~
In file included from /opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/iostream:39,
                 from <source>:2:
/opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/ostream:524:5: note: candidate: 'template<class _Traits> std::basic_ostream<char, _Traits>& std::operator<<(std::basic_ostream<char, _Traits>&, char)'
  524 |     operator<<(basic_ostream<char, _Traits>& __out, char __c)
      |     ^~~~~~~~
/opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/ostream:524:5: note:   template argument deduction/substitution failed:
<source>:44:30: note:   'JsonRecord' is not derived from 'std::basic_ostream<char, _Traits>'
   44 |     mylog << "hello" << std::endl;
      |                              ^~~~
In file included from /opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/iostream:39,
                 from <source>:2:
/opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/ostream:530:5: note: candidate: 'template<class _Traits> std::basic_ostream<char, _Traits>& std::operator<<(std::basic_ostream<char, _Traits>&, signed char)'
  530 |     operator<<(basic_ostream<char, _Traits>& __out, signed char __c)
      |     ^~~~~~~~
/opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/ostream:530:5: note:   template argument deduction/substitution failed:
<source>:44:30: note:   'JsonRecord' is not derived from 'std::basic_ostream<char, _Traits>'
   44 |     mylog << "hello" << std::endl;
      |                              ^~~~
In file included from /opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/iostream:39,
                 from <source>:2:
/opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/ostream:535:5: note: candidate: 'template<class _Traits> std::basic_ostream<char, _Traits>& std::operator<<(std::basic_ostream<char, _Traits>&, unsigned char)'
  535 |     operator<<(basic_ostream<char, _Traits>& __out, unsigned char __c)
      |     ^~~~~~~~
/opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/ostream:535:5: note:   template argument deduction/substitution failed:
<source>:44:30: note:   'JsonRecord' is not derived from 'std::basic_ostream<char, _Traits>'
   44 |     mylog << "hello" << std::endl;
      |                              ^~~~
In file included from /opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/iostream:39,
                 from <source>:2:
/opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/ostream:594:5: note: candidate: 'template<class _CharT, class _Traits> std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&, const _CharT*)'
  594 |     operator<<(basic_ostream<_CharT, _Traits>& __out, const _CharT* __s)
      |     ^~~~~~~~
/opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/ostream:594:5: note:   template argument deduction/substitution failed:
<source>:44:30: note:   'JsonRecord' is not derived from 'std::basic_ostream<_CharT, _Traits>'
   44 |     mylog << "hello" << std::endl;
      |                              ^~~~
In file included from /opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/ostream:829,
                 from /opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/iostream:39,
                 from <source>:2:
/opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/bits/ostream.tcc:321:5: note: candidate: 'template<class _CharT, class _Traits> std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&, const char*)'
  321 |     operator<<(basic_ostream<_CharT, _Traits>& __out, const char* __s)
      |     ^~~~~~~~
/opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/bits/ostream.tcc:321:5: note:   template argument deduction/substitution failed:
<source>:44:30: note:   'JsonRecord' is not derived from 'std::basic_ostream<_CharT, _Traits>'
   44 |     mylog << "hello" << std::endl;
      |                              ^~~~
In file included from /opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/iostream:39,
                 from <source>:2:
/opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/ostream:611:5: note: candidate: 'template<class _Traits> std::basic_ostream<char, _Traits>& std::operator<<(std::basic_ostream<char, _Traits>&, const char*)'
  611 |     operator<<(basic_ostream<char, _Traits>& __out, const char* __s)
      |     ^~~~~~~~
/opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/ostream:611:5: note:   template argument deduction/substitution failed:
<source>:44:30: note:   'JsonRecord' is not derived from 'std::basic_ostream<char, _Traits>'
   44 |     mylog << "hello" << std::endl;
      |                              ^~~~
In file included from /opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/iostream:39,
                 from <source>:2:
/opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/ostream:624:5: note: candidate: 'template<class _Traits> std::basic_ostream<char, _Traits>& std::operator<<(std::basic_ostream<char, _Traits>&, const signed char*)'
  624 |     operator<<(basic_ostream<char, _Traits>& __out, const signed char* __s)
      |     ^~~~~~~~
/opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/ostream:624:5: note:   template argument deduction/substitution failed:
<source>:44:30: note:   'JsonRecord' is not derived from 'std::basic_ostream<char, _Traits>'
   44 |     mylog << "hello" << std::endl;
      |                              ^~~~
In file included from /opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/iostream:39,
                 from <source>:2:
/opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/ostream:629:5: note: candidate: 'template<class _Traits> std::basic_ostream<char, _Traits>& std::operator<<(std::basic_ostream<char, _Traits>&, const unsigned char*)'
  629 |     operator<<(basic_ostream<char, _Traits>& __out, const unsigned char* __s)
      |     ^~~~~~~~
/opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/ostream:629:5: note:   template argument deduction/substitution failed:
<source>:44:30: note:   'JsonRecord' is not derived from 'std::basic_ostream<char, _Traits>'
   44 |     mylog << "hello" << std::endl;
      |                              ^~~~
In file included from /opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/iostream:39,
                 from <source>:2:
/opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/ostream:750:5: note: candidate: 'template<class _Ostream, class _Tp> _Ostream&& std::operator<<(_Ostream&&, const _Tp&)'
  750 |     operator<<(_Ostream&& __os, const _Tp& __x)
      |     ^~~~~~~~
/opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/ostream:750:5: note:   template argument deduction/substitution failed:
<source>:44:30: note:   couldn't deduce template parameter '_Tp'
   44 |     mylog << "hello" << std::endl;
      |                              ^~~~
In file included from /opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/memory:77,
                 from <source>:4:
/opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/bits/shared_ptr.h:70:5: note: candidate: 'template<class _Ch, class _Tr, class _Tp, __gnu_cxx::_Lock_policy _Lp> std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&, const std::__shared_ptr<_Tp, _Lp>&)'
   70 |     operator<<(std::basic_ostream<_Ch, _Tr>& __os,
      |     ^~~~~~~~
/opt/compiler-explorer/gcc-11.4.0/include/c++/11.4.0/bits/shared_ptr.h:70:5: note:   template argument deduction/substitution failed:
<source>:44:30: note:   'JsonRecord' is not derived from 'std::basic_ostream<_CharT, _Traits>'
   44 |     mylog << "hello" << std::endl;
      |                              ^~~~
Compiler returned: 1

My thought is that the compiler should be able to determine the type of the std::endl since it's predetermined, or am I missing something important?

Upvotes: 3

Views: 109

Answers (1)

wohlstad
wohlstad

Reputation: 29467

std::endl is actually a function template (like many other I/O manipulators).
Therefore the compiler will not deduce its type when used with operator<<.

std::basic_ostream has the same issue, and you can solve it similarly to it:

std::basic_ostream::operator<< has special overloads (#18-20) to take care of such I/O manipulators:

basic_ostream& operator<<(
    std::ios_base& (*func)(std::ios_base&) );   (18)    
basic_ostream& operator<<(
    std::basic_ios<CharT, Traits>& (*func)(std::basic_ios<CharT, Traits>&) ); (19)  
basic_ostream& operator<<(
    std::basic_ostream<CharT, Traits>& (*func)
        (std::basic_ostream<CharT, Traits>&) ); (20) 

And their description is:

18-20) Calls func(*this). These overloads are used to implement output I/O manipulators such as std::endl.

(emphasis is mine)

In your case add the following to JsonRecord (the equivalents of overloads 18-20 above):

auto operator<<(std::ios_base& (*func)(std::ios_base&)) -> JsonRecord&
{
    func(_ostr);
    return *this;
}

auto operator<<(std::basic_ios<char>& (*func)(std::basic_ios<char>&)) -> JsonRecord&
{
    func(_ostr);
    return *this;
}

auto operator<<(std::basic_ostream<char>& (*func)(std::basic_ostream<char>&)) -> JsonRecord&
{
    func(_ostr);
    return *this;
}

Live demo

Upvotes: 4

Related Questions