Reputation: 4960
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:
#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
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;
}
Upvotes: 4