https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80662
Jonathan Wakely <redi at gcc dot gnu.org> changed: What |Removed |Added ---------------------------------------------------------------------------- CC|jwakely.gcc at gmail dot com |ville at gcc dot gnu.org --- Comment #2 from Jonathan Wakely <redi at gcc dot gnu.org> --- There's no implicit cast, it's because my_stream{} is an rvalue, so uses the overload for rvalue streams: template <class charT, class traits, class T> basic_ostream<charT, traits>& operator<<(basic_ostream<charT, traits>&& os, const T& x); This returns the base class (as all the standard operator<< overloads do). The only difference between gcc 6 and 7 is that operator is constrained, as required by https://wg21.link/lwg2534 and G++ selects the constrained overload rather than the member my_stream::operator<< (maybe because it's more specialized, not sure yet). Clang always selects the member function, whether the other one is constrained or not. EDG incorrectly says there's an ambiguous overload. So I don't think this is a libstdc++ bug, the changes to the libstdc++ code are correct and required for conformance. G++ seems to be choosing the wrong overload. Reduced: namespace std { // <type_traits> struct true_type { static constexpr bool value = true; }; struct false_type { static constexpr bool value = false; }; template<bool Cond, typename T = void> struct enable_if { using type = T; }; template<> struct enable_if<false> { }; template<typename T, typename U> struct is_same : false_type { }; template<typename T> struct is_same<T, T> : true_type { }; template<typename T> struct remove_reference { using type = T; }; template<typename T> struct remove_reference<T&> { using type = T; }; template<typename T> struct remove_reference<T&&> { using type = T; }; template<typename T> struct is_lvalue_reference : false_type { }; template<typename T> struct is_lvalue_reference<T&> : true_type { }; template<typename T> T declval(); template<typename...> using void_t = void; template<bool C, typename If, typename Else> struct conditional { using type = If; }; template<typename If, typename Else> struct conditional<false, If, Else> { using type = Else; }; template<typename...> struct __and_; template<typename T, typename U> struct __and_<T, U> : conditional<T::value, U, false_type>::type { }; template<typename T, typename U, typename V> struct __and_<T, U, V> : conditional<T::value, __and_<U, V>, false_type>::type { }; template<typename T> struct __not_ : conditional<T::value, false_type, true_type>::type { }; // <utility> template<typename T> T&& forward(T& t) { return static_cast<T&&>(t); } // <string> template<typename C> struct char_traits { }; struct string { string(const char* s) : str(s) { } const char* str; }; // <ostream> template<typename C, typename T = char_traits<C>> struct basic_ostream { basic_ostream& operator<<(const char*) { return *this; } }; using ostream = basic_ostream<char>; inline ostream operator<<(ostream& os, const string& s) { os << s.str; return os; } #ifndef UNCONSTRAINED // GCC 7 code template<typename _Tp> struct __is_convertible_to_basic_ostream { template<typename _Ch, typename _Up> static basic_ostream<_Ch, _Up>& __check(basic_ostream<_Ch, _Up>*); static void __check(...); public: using ostream_type = decltype(__check(declval<typename remove_reference<_Tp>::type*>())); constexpr static bool value = !is_same<ostream_type, void>::value; }; template<typename _Ostream, typename _Tp, typename = void> struct __is_insertable : false_type {}; template<typename _Ostream, typename _Tp> struct __is_insertable<_Ostream, _Tp, void_t<decltype(declval<_Ostream&>() << declval<const _Tp&>())>> : true_type {}; template<typename _Ostream, typename _Tp> inline typename enable_if<__and_<__not_<is_lvalue_reference<_Ostream>>, __is_convertible_to_basic_ostream<_Ostream>, __is_insertable<_Ostream&, const _Tp&>>::value, typename __is_convertible_to_basic_ostream< _Ostream>::ostream_type>::type operator<<(_Ostream&& __os, const _Tp& __x) { __os << __x; return __os; } #else // GCC 6 code template<typename _CharT, typename _Traits, typename _Tp> inline basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>&& __os, const _Tp& __x) { __os << __x; return __os; } #endif } struct my_stream : public std::ostream { template<typename T> my_stream& operator<<(T&& value) { std::ostream::operator<<(std::forward<T>(value)); return *this; } }; int main() { my_stream& s = (my_stream{} << "hello world"); }