[Bug libstdc++/80662] libstdc++ basic_string casting oddity
redi at gcc dot gnu.org
gcc-bugzilla@gcc.gnu.org
Mon May 8 10:53:00 GMT 2017
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");
}
More information about the Gcc-bugs
mailing list