I noticed that the std::basic_ostream
operator<<
overload for rvalue ostream
s is not selected for a temporary std::ostringstream
in combination with std::unique_ptr
.
#include <iostream>
#include <memory>
#include <sstream>
std::ostream& operator<<(std::ostream& os, const std::unique_ptr<int>& /*res*/)
{
os << "Let's print something" << std::endl;
return os;
}
int main()
{
std::unique_ptr<int> res;
std::ostringstream {} << res;
return 0;
}
Above code compiled with GCC 14.2 and C++17 results in
error: no match for 'operator<<' (operand types are 'std::ostringstream' {aka 'std::__cxx11::basic_ostringstream<char>'} and 'std::unique_ptr<int>')
It is clear that the user-defined operator<<
overload cannot be selected directly as it takes a non-const lvalue reference to an ostream that cannot be bound to a temporary (rvalue) ostream.
The overload that I would expect to be taken is
template< class Ostream, class T >
Ostream&& operator<<( Ostream&& os, const T& value );
from std::basic_ostream
https://en.cppreference.com/w/cpp/io/basic_ostream/operator_ltlt2
When compiling the code above, GCC outputs the following error for above operator
/opt/compiler-explorer/gcc-14.2.0/include/c++/14.2.0/ostream:807:5: note: candidate: 'template<class _Ostream, class _Tp> _Ostream&& std::operator<<(_Ostream&&, const _Tp&)'
807 | operator<<(_Ostream&& __os, const _Tp& __x)
| ^~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/include/c++/14.2.0/ostream:807:5: note: template argument deduction/substitution failed:
/opt/compiler-explorer/gcc-14.2.0/include/c++/14.2.0/ostream: In substitution of 'template<class _Ostream, class _Tp> _Ostream&& std::operator<<(_Ostream&&, const _Tp&) [with _Ostream = std::__cxx11::basic_ostringstream<char>; _Tp = std::unique_ptr<int>]':
<source>:14:30: required from here
14 | std::ostringstream {} << res;
| ^~~
/opt/compiler-explorer/gcc-14.2.0/include/c++/14.2.0/ostream:791:46: error: no match for 'operator<<' (operand types are 'std::__cxx11::basic_ostringstream<char>' and 'const std::unique_ptr<int>')
791 | = decltype(std::declval<_Os&>() << std::declval<const _Tp&>())>
| ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Can anyone explain why decltype(std::declval<_Os&>() << std::declval<const _Tp&>())
fails for _Os = std::ostringstream
and _Tp = std::unique_ptr<int>
?
You can try it out here: https://godbolt.org/z/vPqEeME8z
Operator overload that takes rvalue reference to output stream is only possible to use if there exists a valid operator overload that could accept lvalue reference. See description from cppreference (emphasis mine):
Calls the appropriate insertion operator, given an rvalue reference to an output stream object (equivalent to
os << value
). This overload participates in overload resolution only if the expressionos << value
is well-formed andOstream
is a class type publicly and unambiguously derived fromstd::ios_base
.
Your operator overload is not considered due to Argument Dependent Lookup rules. os << value
cannot find this overload, so the rvalue-ref overload is not considered.
In C++20 std::unique_ptr
received its own operator<<
overload, this one is findable via ADL, thus rvalue-ref operator is valid and is selected.