c++c++17operator-overloadingunique-ptrostringstream

operator<< overload not selected for rvalue std::ostringstream and std::unique_ptr


I noticed that the std::basic_ostream operator<< overload for rvalue ostreams 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


Solution

  • 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 expression os << value is well-formed and Ostream is a class type publicly and unambiguously derived from std::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.