libstdc++: Implement LWG 1203 for rvalue iostreams
This implements the resolution of LWG 1203 so that the constraints for rvalue stream insertion/extraction are simpler, and the return type is the original rvalue stream type not its base class. Signed-off-by: Jonathan Wakely <jwakely@redhat.com> libstdc++-v3/ChangeLog: * include/std/istream (operator>>(Istream&&, x&)): Simplify, as per LWG 1203. * include/std/ostream (operator<<(Ostream&&, const x&)): Likewise. * testsuite/27_io/basic_istream/extractors_character/char/lwg2499_neg.cc: Adjust dg-error pattern. * testsuite/27_io/basic_istream/extractors_character/wchar_t/lwg2499_neg.cc: Likewise. * testsuite/27_io/basic_istream/extractors_other/char/4.cc: Define is_extractable trait to replace std::__is_extractable. Make it work with rvalue streams as well as lvalues, to replace f() and g() helper functions. * testsuite/27_io/basic_istream/extractors_other/wchar_t/4.cc: Likewise. * testsuite/27_io/basic_ostream/inserters_other/char/6.cc: Define is_insertable trait to replace std::__is_insertable. Make it work with rvalue streams as well as lvalues, to replace f() and g() helper functions. * testsuite/27_io/basic_ostream/inserters_other/wchar_t/6.cc: Likewise. * testsuite/27_io/filesystem/path/io/dr2989.cc: Prune additional errors from new constraints. * testsuite/27_io/rvalue_streams-2.cc: Remove PR 80675 checks, which are no longer expected to compile. * testsuite/27_io/rvalue_streams.cc: Adjust existing test. Verify LWG 1203 changes.
This commit is contained in:
parent
0d501c3385
commit
aa475c4ac8
@ -953,80 +953,34 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
ws(basic_istream<_CharT, _Traits>& __is);
|
||||
|
||||
#if __cplusplus >= 201103L
|
||||
template<typename _Ch, typename _Up>
|
||||
basic_istream<_Ch, _Up>&
|
||||
__is_convertible_to_basic_istream_test(basic_istream<_Ch, _Up>*);
|
||||
|
||||
template<typename _Tp, typename = void>
|
||||
struct __is_convertible_to_basic_istream_impl
|
||||
{
|
||||
using __istream_type = void;
|
||||
};
|
||||
|
||||
template<typename _Tp>
|
||||
using __do_is_convertible_to_basic_istream_impl =
|
||||
decltype(__is_convertible_to_basic_istream_test
|
||||
(declval<typename remove_reference<_Tp>::type*>()));
|
||||
|
||||
template<typename _Tp>
|
||||
struct __is_convertible_to_basic_istream_impl
|
||||
<_Tp,
|
||||
__void_t<__do_is_convertible_to_basic_istream_impl<_Tp>>>
|
||||
{
|
||||
using __istream_type =
|
||||
__do_is_convertible_to_basic_istream_impl<_Tp>;
|
||||
};
|
||||
|
||||
template<typename _Tp>
|
||||
struct __is_convertible_to_basic_istream
|
||||
: __is_convertible_to_basic_istream_impl<_Tp>
|
||||
{
|
||||
public:
|
||||
using type = __not_<is_void<
|
||||
typename __is_convertible_to_basic_istream_impl<_Tp>::__istream_type>>;
|
||||
constexpr static bool value = type::value;
|
||||
};
|
||||
|
||||
template<typename _Istream, typename _Tp, typename = void>
|
||||
struct __is_extractable : false_type {};
|
||||
|
||||
template<typename _Istream, typename _Tp>
|
||||
struct __is_extractable<_Istream, _Tp,
|
||||
__void_t<decltype(declval<_Istream&>()
|
||||
>> declval<_Tp>())>>
|
||||
: true_type {};
|
||||
|
||||
template<typename _Istream>
|
||||
using __rvalue_istream_type =
|
||||
typename __is_convertible_to_basic_istream<
|
||||
_Istream>::__istream_type;
|
||||
|
||||
// [27.7.1.6] Rvalue stream extraction
|
||||
// C++11 27.7.2.6 Rvalue stream extraction [istream.rvalue]
|
||||
// _GLIBCXX_RESOLVE_LIB_DEFECTS
|
||||
// 2328. Rvalue stream extraction should use perfect forwarding
|
||||
// 1203. More useful rvalue stream insertion
|
||||
|
||||
// SFINAE helper to check constraints for operator>>(Istream&&, T&&).
|
||||
// If the constraints are satisfied, it is an alias for Istream&&.
|
||||
template<typename _Is, typename _Tp,
|
||||
typename = decltype(std::__rval_streamable<_Is>()
|
||||
>> std::declval<_Tp>())>
|
||||
using __rvalue_stream_extraction_t = _Is&&;
|
||||
|
||||
/**
|
||||
* @brief Generic extractor for rvalue stream
|
||||
* @param __is An input stream.
|
||||
* @param __x A reference to the extraction target.
|
||||
* @return is
|
||||
* @return __is
|
||||
*
|
||||
* This is just a forwarding function to allow extraction from
|
||||
* rvalue streams since they won't bind to the extractor functions
|
||||
* that take an lvalue reference.
|
||||
*/
|
||||
template<typename _Istream, typename _Tp>
|
||||
inline
|
||||
typename enable_if<__and_<__not_<is_lvalue_reference<_Istream>>,
|
||||
__is_convertible_to_basic_istream<_Istream>,
|
||||
__is_extractable<
|
||||
__rvalue_istream_type<_Istream>,
|
||||
_Tp&&>>::value,
|
||||
__rvalue_istream_type<_Istream>>::type
|
||||
inline __rvalue_stream_extraction_t<_Istream, _Tp>
|
||||
operator>>(_Istream&& __is, _Tp&& __x)
|
||||
{
|
||||
__rvalue_istream_type<_Istream> __ret_is = __is;
|
||||
__ret_is >> std::forward<_Tp>(__x);
|
||||
return __ret_is;
|
||||
__is >> std::forward<_Tp>(__x);
|
||||
return std::move(__is);
|
||||
}
|
||||
#endif // C++11
|
||||
|
||||
|
@ -704,77 +704,52 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
{ return __os.flush(); }
|
||||
|
||||
#if __cplusplus >= 201103L
|
||||
template<typename _Ch, typename _Up>
|
||||
basic_ostream<_Ch, _Up>&
|
||||
__is_convertible_to_basic_ostream_test(basic_ostream<_Ch, _Up>*);
|
||||
|
||||
template<typename _Tp, typename = void>
|
||||
struct __is_convertible_to_basic_ostream_impl
|
||||
{
|
||||
using __ostream_type = void;
|
||||
};
|
||||
// C++11 27.7.3.9 Rvalue stream insertion [ostream.rvalue]
|
||||
// _GLIBCXX_RESOLVE_LIB_DEFECTS
|
||||
// 1203. More useful rvalue stream insertion
|
||||
|
||||
// SFINAE helper to check constraints for operator<<(Ostream&&, const T&).
|
||||
// If Ostream is publicly and unambiguously derived from ios_base, then
|
||||
// __rval_streamable<Ostream>() is equivalent to declval<Ostream&>().
|
||||
// Otherwise, it results in a substitution failure. Specifically, it will
|
||||
// fail if Ostream is an lvalue reference or the same type as ios_base.
|
||||
// Use concepts if possible because they're cheaper to evaluate.
|
||||
#if __cpp_lib_concepts
|
||||
template<typename _Tp>
|
||||
using __do_is_convertible_to_basic_ostream_impl =
|
||||
decltype(__is_convertible_to_basic_ostream_test
|
||||
(declval<typename remove_reference<_Tp>::type*>()));
|
||||
requires (!is_same_v<_Tp, ios_base>)
|
||||
&& requires (_Tp* __t, ios_base* __b) { __b = __t; }
|
||||
_Tp&
|
||||
__rval_streamable();
|
||||
#else
|
||||
template<typename _Tp,
|
||||
typename = _Require<__not_<__is_one_of<_Tp, _Tp&, ios_base>>>>
|
||||
_Tp&
|
||||
__rval_streamable(ios_base* = (_Tp*)nullptr);
|
||||
#endif
|
||||
|
||||
template<typename _Tp>
|
||||
struct __is_convertible_to_basic_ostream_impl
|
||||
<_Tp,
|
||||
__void_t<__do_is_convertible_to_basic_ostream_impl<_Tp>>>
|
||||
{
|
||||
using __ostream_type =
|
||||
__do_is_convertible_to_basic_ostream_impl<_Tp>;
|
||||
};
|
||||
|
||||
template<typename _Tp>
|
||||
struct __is_convertible_to_basic_ostream
|
||||
: __is_convertible_to_basic_ostream_impl<_Tp>
|
||||
{
|
||||
public:
|
||||
using type = __not_<is_void<
|
||||
typename __is_convertible_to_basic_ostream_impl<_Tp>::__ostream_type>>;
|
||||
constexpr static bool value = type::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>
|
||||
using __rvalue_ostream_type =
|
||||
typename __is_convertible_to_basic_ostream<
|
||||
_Ostream>::__ostream_type;
|
||||
// SFINAE helper to check constraints for operator<<(Ostream&&, const T&).
|
||||
// If the constraints are satisfied, it is an alias for Ostream&&.
|
||||
template<typename _Os, typename _Tp,
|
||||
typename = decltype(std::__rval_streamable<_Os>()
|
||||
<< std::declval<const _Tp&>())>
|
||||
using __rvalue_stream_insertion_t = _Os&&;
|
||||
|
||||
/**
|
||||
* @brief Generic inserter for rvalue stream
|
||||
* @param __os An input stream.
|
||||
* @param __x A reference to the object being inserted.
|
||||
* @return os
|
||||
* @return __os
|
||||
*
|
||||
* This is just a forwarding function to allow insertion to
|
||||
* rvalue streams since they won't bind to the inserter functions
|
||||
* that take an lvalue reference.
|
||||
*/
|
||||
template<typename _Ostream, typename _Tp>
|
||||
inline
|
||||
typename enable_if<__and_<__not_<is_lvalue_reference<_Ostream>>,
|
||||
__is_convertible_to_basic_ostream<_Ostream>,
|
||||
__is_insertable<
|
||||
__rvalue_ostream_type<_Ostream>,
|
||||
const _Tp&>>::value,
|
||||
__rvalue_ostream_type<_Ostream>>::type
|
||||
inline __rvalue_stream_insertion_t<_Ostream, _Tp>
|
||||
operator<<(_Ostream&& __os, const _Tp& __x)
|
||||
{
|
||||
__rvalue_ostream_type<_Ostream> __ret_os = __os;
|
||||
__ret_os << __x;
|
||||
return __ret_os;
|
||||
__os << __x;
|
||||
return std::move(__os);
|
||||
}
|
||||
|
||||
#if __cplusplus > 201703L && _GLIBCXX_USE_CXX11_ABI
|
||||
|
@ -26,9 +26,9 @@
|
||||
void
|
||||
test01(std::istream& in, char* pc, signed char* sc, unsigned char* uc)
|
||||
{
|
||||
in >> pc; // { dg-error "here" }
|
||||
in >> sc; // { dg-error "here" }
|
||||
in >> uc; // { dg-error "here" }
|
||||
in >> pc; // { dg-error "no match" }
|
||||
in >> sc; // { dg-error "no match" }
|
||||
in >> uc; // { dg-error "no match" }
|
||||
}
|
||||
|
||||
struct CT : std::char_traits<char> { };
|
||||
@ -37,9 +37,9 @@ void
|
||||
test02(std::basic_istream<char, CT>& in, char* pc, signed char* sc,
|
||||
unsigned char* uc)
|
||||
{
|
||||
in >> pc; // { dg-error "here" }
|
||||
in >> sc; // { dg-error "here" }
|
||||
in >> uc; // { dg-error "here" }
|
||||
in >> pc; // { dg-error "no match" }
|
||||
in >> sc; // { dg-error "no match" }
|
||||
in >> uc; // { dg-error "no match" }
|
||||
}
|
||||
|
||||
// { dg-excess-errors "" }
|
||||
|
@ -26,7 +26,7 @@
|
||||
void
|
||||
test01(std::wistream& in, wchar_t* wc)
|
||||
{
|
||||
in >> wc; // { dg-error "here" }
|
||||
in >> wc; // { dg-error "no match" }
|
||||
}
|
||||
|
||||
struct WT : std::char_traits<wchar_t> { };
|
||||
@ -34,7 +34,7 @@ struct WT : std::char_traits<wchar_t> { };
|
||||
void
|
||||
test02(std::basic_istream<wchar_t, WT>& in, wchar_t* wc)
|
||||
{
|
||||
in >> wc; // { dg-error "here" }
|
||||
in >> wc; // { dg-error "no match" }
|
||||
}
|
||||
|
||||
// { dg-excess-errors "" }
|
||||
|
@ -21,6 +21,21 @@
|
||||
|
||||
#include <sstream>
|
||||
|
||||
template<typename Istream, typename T, typename = void>
|
||||
struct is_extractable : std::false_type
|
||||
{ };
|
||||
|
||||
template<typename> using void_t = void;
|
||||
|
||||
template<typename Istream, typename T>
|
||||
using extract_result
|
||||
= decltype(std::declval<Istream>() >> std::declval<const T&>());
|
||||
|
||||
template<typename Istream, typename T>
|
||||
struct is_extractable<Istream, T, void_t<extract_result<Istream, T>>>
|
||||
: std::true_type
|
||||
{ };
|
||||
|
||||
struct X {};
|
||||
std::istream& operator>>(std::istream&, X&) = delete;
|
||||
|
||||
@ -30,20 +45,6 @@ std::istream& operator>>(std::istream& is, Y&&) {return is;}
|
||||
|
||||
struct Z{};
|
||||
|
||||
template <class T>
|
||||
auto f(T&&) -> decltype(void(std::declval<std::istream&>()
|
||||
>> std::declval<T&&>()),
|
||||
std::true_type());
|
||||
|
||||
std::false_type f(...);
|
||||
|
||||
template <class T>
|
||||
auto g(T&&) -> decltype(void(std::declval<std::istream&&>()
|
||||
>> std::declval<T&&>()),
|
||||
std::true_type());
|
||||
|
||||
std::false_type g(...);
|
||||
|
||||
void test01()
|
||||
{
|
||||
Y y;
|
||||
@ -52,42 +53,18 @@ void test01()
|
||||
is >> Y();
|
||||
std::istringstream() >> y;
|
||||
std::istringstream() >> Y();
|
||||
static_assert(!std::__is_extractable<std::istream&, X&>::value, "");
|
||||
static_assert(!std::__is_extractable<std::istream&&, X&>::value, "");
|
||||
static_assert(!std::__is_extractable<std::istream&, X&&>::value, "");
|
||||
static_assert(!std::__is_extractable<std::istream&&, X&&>::value, "");
|
||||
static_assert(std::__is_extractable<std::istream&, Y&>::value, "");
|
||||
static_assert(std::__is_extractable<std::istream&&, Y&>::value, "");
|
||||
static_assert(std::__is_extractable<std::istream&, Y&&>::value, "");
|
||||
static_assert(std::__is_extractable<std::istream&&, Y&&>::value, "");
|
||||
static_assert(!std::__is_extractable<std::istream&, Z&>::value, "");
|
||||
static_assert(!std::__is_extractable<std::istream&&, Z&>::value, "");
|
||||
static_assert(!std::__is_extractable<std::istream&, Z&&>::value, "");
|
||||
static_assert(!std::__is_extractable<std::istream&&, Z&&>::value, "");
|
||||
static_assert(std::is_same<decltype(f(std::declval<X&>())),
|
||||
std::false_type>::value, "");
|
||||
static_assert(std::is_same<decltype(f(std::declval<X&&>())),
|
||||
std::false_type>::value, "");
|
||||
static_assert(std::is_same<decltype(f(std::declval<Y&>())),
|
||||
std::true_type>::value, "");
|
||||
static_assert(std::is_same<decltype(f(std::declval<Y&&>())),
|
||||
std::true_type>::value, "");
|
||||
static_assert(std::is_same<decltype(f(std::declval<Z&>())),
|
||||
std::false_type>::value, "");
|
||||
static_assert(std::is_same<decltype(f(std::declval<Z&&>())),
|
||||
std::false_type>::value, "");
|
||||
static_assert(std::is_same<decltype(g(std::declval<X&>())),
|
||||
std::false_type>::value, "");
|
||||
static_assert(std::is_same<decltype(g(std::declval<X&&>())),
|
||||
std::false_type>::value, "");
|
||||
static_assert(std::is_same<decltype(g(std::declval<Y&>())),
|
||||
std::true_type>::value, "");
|
||||
static_assert(std::is_same<decltype(g(std::declval<Y&&>())),
|
||||
std::true_type>::value, "");
|
||||
static_assert(std::is_same<decltype(g(std::declval<Z&>())),
|
||||
std::false_type>::value, "");
|
||||
static_assert(std::is_same<decltype(g(std::declval<Z&&>())),
|
||||
std::false_type>::value, "");
|
||||
static_assert(!is_extractable<std::istream&, X&>::value, "");
|
||||
static_assert(!is_extractable<std::istream&&, X&>::value, "");
|
||||
static_assert(!is_extractable<std::istream&, X&&>::value, "");
|
||||
static_assert(!is_extractable<std::istream&&, X&&>::value, "");
|
||||
static_assert(is_extractable<std::istream&, Y&>::value, "");
|
||||
static_assert(is_extractable<std::istream&&, Y&>::value, "");
|
||||
static_assert(is_extractable<std::istream&, Y&&>::value, "");
|
||||
static_assert(is_extractable<std::istream&&, Y&&>::value, "");
|
||||
static_assert(!is_extractable<std::istream&, Z&>::value, "");
|
||||
static_assert(!is_extractable<std::istream&&, Z&>::value, "");
|
||||
static_assert(!is_extractable<std::istream&, Z&&>::value, "");
|
||||
static_assert(!is_extractable<std::istream&&, Z&&>::value, "");
|
||||
}
|
||||
|
||||
int main()
|
||||
|
@ -21,6 +21,21 @@
|
||||
|
||||
#include <sstream>
|
||||
|
||||
template<typename Istream, typename T, typename = void>
|
||||
struct is_extractable : std::false_type
|
||||
{ };
|
||||
|
||||
template<typename> using void_t = void;
|
||||
|
||||
template<typename Istream, typename T>
|
||||
using extract_result
|
||||
= decltype(std::declval<Istream>() >> std::declval<const T&>());
|
||||
|
||||
template<typename Istream, typename T>
|
||||
struct is_extractable<Istream, T, void_t<extract_result<Istream, T>>>
|
||||
: std::true_type
|
||||
{ };
|
||||
|
||||
struct X {};
|
||||
std::wistream& operator>>(std::wistream&, X&) = delete;
|
||||
|
||||
@ -30,20 +45,6 @@ std::wistream& operator>>(std::wistream& is, Y&&) {return is;}
|
||||
|
||||
struct Z{};
|
||||
|
||||
template <class T>
|
||||
auto f(T&&) -> decltype(void(std::declval<std::wistream&>()
|
||||
>> std::declval<T&&>()),
|
||||
std::true_type());
|
||||
|
||||
std::false_type f(...);
|
||||
|
||||
template <class T>
|
||||
auto g(T&&) -> decltype(void(std::declval<std::wistream&&>()
|
||||
>> std::declval<T&&>()),
|
||||
std::true_type());
|
||||
|
||||
std::false_type g(...);
|
||||
|
||||
void test01()
|
||||
{
|
||||
Y y;
|
||||
@ -52,42 +53,18 @@ void test01()
|
||||
is >> Y();
|
||||
std::wistringstream() >> y;
|
||||
std::wistringstream() >> Y();
|
||||
static_assert(!std::__is_extractable<std::wistream&, X&>::value, "");
|
||||
static_assert(!std::__is_extractable<std::wistream&&, X&>::value, "");
|
||||
static_assert(!std::__is_extractable<std::wistream&, X&&>::value, "");
|
||||
static_assert(!std::__is_extractable<std::wistream&&, X&&>::value, "");
|
||||
static_assert(std::__is_extractable<std::wistream&, Y&>::value, "");
|
||||
static_assert(std::__is_extractable<std::wistream&&, Y&>::value, "");
|
||||
static_assert(std::__is_extractable<std::wistream&, Y&&>::value, "");
|
||||
static_assert(std::__is_extractable<std::wistream&&, Y&&>::value, "");
|
||||
static_assert(!std::__is_extractable<std::wistream&, Z&>::value, "");
|
||||
static_assert(!std::__is_extractable<std::wistream&&, Z&>::value, "");
|
||||
static_assert(!std::__is_extractable<std::wistream&, Z&&>::value, "");
|
||||
static_assert(!std::__is_extractable<std::wistream&&, Z&&>::value, "");
|
||||
static_assert(std::is_same<decltype(f(std::declval<X&>())),
|
||||
std::false_type>::value, "");
|
||||
static_assert(std::is_same<decltype(f(std::declval<X&&>())),
|
||||
std::false_type>::value, "");
|
||||
static_assert(std::is_same<decltype(f(std::declval<Y&>())),
|
||||
std::true_type>::value, "");
|
||||
static_assert(std::is_same<decltype(f(std::declval<Y&&>())),
|
||||
std::true_type>::value, "");
|
||||
static_assert(std::is_same<decltype(f(std::declval<Z&>())),
|
||||
std::false_type>::value, "");
|
||||
static_assert(std::is_same<decltype(f(std::declval<Z&&>())),
|
||||
std::false_type>::value, "");
|
||||
static_assert(std::is_same<decltype(g(std::declval<X&>())),
|
||||
std::false_type>::value, "");
|
||||
static_assert(std::is_same<decltype(g(std::declval<X&&>())),
|
||||
std::false_type>::value, "");
|
||||
static_assert(std::is_same<decltype(g(std::declval<Y&>())),
|
||||
std::true_type>::value, "");
|
||||
static_assert(std::is_same<decltype(g(std::declval<Y&&>())),
|
||||
std::true_type>::value, "");
|
||||
static_assert(std::is_same<decltype(g(std::declval<Z&>())),
|
||||
std::false_type>::value, "");
|
||||
static_assert(std::is_same<decltype(g(std::declval<Z&&>())),
|
||||
std::false_type>::value, "");
|
||||
static_assert(!is_extractable<std::wistream&, X&>::value, "");
|
||||
static_assert(!is_extractable<std::wistream&&, X&>::value, "");
|
||||
static_assert(!is_extractable<std::wistream&, X&&>::value, "");
|
||||
static_assert(!is_extractable<std::wistream&&, X&&>::value, "");
|
||||
static_assert(is_extractable<std::wistream&, Y&>::value, "");
|
||||
static_assert(is_extractable<std::wistream&&, Y&>::value, "");
|
||||
static_assert(is_extractable<std::wistream&, Y&&>::value, "");
|
||||
static_assert(is_extractable<std::wistream&&, Y&&>::value, "");
|
||||
static_assert(!is_extractable<std::wistream&, Z&>::value, "");
|
||||
static_assert(!is_extractable<std::wistream&&, Z&>::value, "");
|
||||
static_assert(!is_extractable<std::wistream&, Z&&>::value, "");
|
||||
static_assert(!is_extractable<std::wistream&&, Z&&>::value, "");
|
||||
}
|
||||
|
||||
int main()
|
||||
|
@ -21,6 +21,22 @@
|
||||
|
||||
#include <sstream>
|
||||
|
||||
template<typename Ostream, typename T, typename = void>
|
||||
struct is_insertable
|
||||
: std::false_type
|
||||
{ };
|
||||
|
||||
template<typename> using void_t = void;
|
||||
|
||||
template<typename Ostream, typename T>
|
||||
using insert_result
|
||||
= decltype(std::declval<Ostream>() << std::declval<const T&>());
|
||||
|
||||
template<typename Ostream, typename T>
|
||||
struct is_insertable<Ostream, T, void_t<insert_result<Ostream, T>>>
|
||||
: std::true_type
|
||||
{ };
|
||||
|
||||
struct X {};
|
||||
std::ostream& operator<<(std::ostream&, const X&) = delete;
|
||||
|
||||
@ -30,20 +46,6 @@ std::ostream& operator<<(std::ostream&& os, const Y&) {return os;}
|
||||
|
||||
struct Z{};
|
||||
|
||||
template <class T>
|
||||
auto f(T&&) -> decltype(void(std::declval<std::ostream&>()
|
||||
<< std::declval<T&&>()),
|
||||
std::true_type());
|
||||
|
||||
std::false_type f(...);
|
||||
|
||||
template <class T>
|
||||
auto g(T&&) -> decltype(void(std::declval<std::ostream&&>()
|
||||
<< std::declval<T&&>()),
|
||||
std::true_type());
|
||||
|
||||
std::false_type g(...);
|
||||
|
||||
void test01()
|
||||
{
|
||||
Y y;
|
||||
@ -52,42 +54,18 @@ void test01()
|
||||
os << Y();
|
||||
std::ostringstream() << y;
|
||||
std::ostringstream() << Y();
|
||||
static_assert(!std::__is_insertable<std::ostream&, X&>::value, "");
|
||||
static_assert(!std::__is_insertable<std::ostream&&, X&>::value, "");
|
||||
static_assert(!std::__is_insertable<std::ostream&, X&&>::value, "");
|
||||
static_assert(!std::__is_insertable<std::ostream&&, X&&>::value, "");
|
||||
static_assert(std::__is_insertable<std::ostream&, Y&>::value, "");
|
||||
static_assert(std::__is_insertable<std::ostream&&, Y&&>::value, "");
|
||||
static_assert(std::__is_insertable<std::ostream&, Y&>::value, "");
|
||||
static_assert(std::__is_insertable<std::ostream&&, Y&&>::value, "");
|
||||
static_assert(!std::__is_insertable<std::ostream&, Z&>::value, "");
|
||||
static_assert(!std::__is_insertable<std::ostream&&, Z&>::value, "");
|
||||
static_assert(!std::__is_insertable<std::ostream&, Z&&>::value, "");
|
||||
static_assert(!std::__is_insertable<std::ostream&&, Z&&>::value, "");
|
||||
static_assert(std::is_same<decltype(f(std::declval<X&>())),
|
||||
std::false_type>::value, "");
|
||||
static_assert(std::is_same<decltype(f(std::declval<X&&>())),
|
||||
std::false_type>::value, "");
|
||||
static_assert(std::is_same<decltype(f(std::declval<Y&>())),
|
||||
std::true_type>::value, "");
|
||||
static_assert(std::is_same<decltype(f(std::declval<Y&&>())),
|
||||
std::true_type>::value, "");
|
||||
static_assert(std::is_same<decltype(f(std::declval<Z&>())),
|
||||
std::false_type>::value, "");
|
||||
static_assert(std::is_same<decltype(f(std::declval<Z&&>())),
|
||||
std::false_type>::value, "");
|
||||
static_assert(std::is_same<decltype(g(std::declval<X&>())),
|
||||
std::false_type>::value, "");
|
||||
static_assert(std::is_same<decltype(g(std::declval<X&&>())),
|
||||
std::false_type>::value, "");
|
||||
static_assert(std::is_same<decltype(g(std::declval<Y&>())),
|
||||
std::true_type>::value, "");
|
||||
static_assert(std::is_same<decltype(g(std::declval<Y&&>())),
|
||||
std::true_type>::value, "");
|
||||
static_assert(std::is_same<decltype(g(std::declval<Z&>())),
|
||||
std::false_type>::value, "");
|
||||
static_assert(std::is_same<decltype(g(std::declval<Z&&>())),
|
||||
std::false_type>::value, "");
|
||||
static_assert(!is_insertable<std::ostream&, X&>::value, "");
|
||||
static_assert(!is_insertable<std::ostream&&, X&>::value, "");
|
||||
static_assert(!is_insertable<std::ostream&, X&&>::value, "");
|
||||
static_assert(!is_insertable<std::ostream&&, X&&>::value, "");
|
||||
static_assert(is_insertable<std::ostream&, Y&>::value, "");
|
||||
static_assert(is_insertable<std::ostream&&, Y&&>::value, "");
|
||||
static_assert(is_insertable<std::ostream&, Y&>::value, "");
|
||||
static_assert(is_insertable<std::ostream&&, Y&&>::value, "");
|
||||
static_assert(!is_insertable<std::ostream&, Z&>::value, "");
|
||||
static_assert(!is_insertable<std::ostream&&, Z&>::value, "");
|
||||
static_assert(!is_insertable<std::ostream&, Z&&>::value, "");
|
||||
static_assert(!is_insertable<std::ostream&&, Z&&>::value, "");
|
||||
}
|
||||
|
||||
int main()
|
||||
|
@ -21,6 +21,22 @@
|
||||
|
||||
#include <sstream>
|
||||
|
||||
template<typename Ostream, typename T, typename = void>
|
||||
struct is_insertable
|
||||
: std::false_type
|
||||
{ };
|
||||
|
||||
template<typename> using void_t = void;
|
||||
|
||||
template<typename Ostream, typename T>
|
||||
using insert_result
|
||||
= decltype(std::declval<Ostream>() << std::declval<const T&>());
|
||||
|
||||
template<typename Ostream, typename T>
|
||||
struct is_insertable<Ostream, T, void_t<insert_result<Ostream, T>>>
|
||||
: std::true_type
|
||||
{ };
|
||||
|
||||
struct X {};
|
||||
std::wostream& operator<<(std::wostream&, const X&) = delete;
|
||||
|
||||
@ -30,20 +46,6 @@ std::wostream& operator<<(std::wostream&& os, const Y&) {return os;}
|
||||
|
||||
struct Z{};
|
||||
|
||||
template <class T>
|
||||
auto f(T&&) -> decltype(void(std::declval<std::wostream&>()
|
||||
<< std::declval<T&&>()),
|
||||
std::true_type());
|
||||
|
||||
std::false_type f(...);
|
||||
|
||||
template <class T>
|
||||
auto g(T&&) -> decltype(void(std::declval<std::wostream&&>()
|
||||
<< std::declval<T&&>()),
|
||||
std::true_type());
|
||||
|
||||
std::false_type g(...);
|
||||
|
||||
void test01()
|
||||
{
|
||||
Y y;
|
||||
@ -52,42 +54,18 @@ void test01()
|
||||
os << Y();
|
||||
std::wostringstream() << y;
|
||||
std::wostringstream() << Y();
|
||||
static_assert(!std::__is_insertable<std::wostream&, X&>::value, "");
|
||||
static_assert(!std::__is_insertable<std::wostream&&, X&>::value, "");
|
||||
static_assert(!std::__is_insertable<std::wostream&, X&&>::value, "");
|
||||
static_assert(!std::__is_insertable<std::wostream&&, X&&>::value, "");
|
||||
static_assert(std::__is_insertable<std::wostream&, Y&>::value, "");
|
||||
static_assert(std::__is_insertable<std::wostream&&, Y&&>::value, "");
|
||||
static_assert(std::__is_insertable<std::wostream&, Y&>::value, "");
|
||||
static_assert(std::__is_insertable<std::wostream&&, Y&&>::value, "");
|
||||
static_assert(!std::__is_insertable<std::wostream&, Z&>::value, "");
|
||||
static_assert(!std::__is_insertable<std::wostream&&, Z&>::value, "");
|
||||
static_assert(!std::__is_insertable<std::wostream&, Z&&>::value, "");
|
||||
static_assert(!std::__is_insertable<std::wostream&&, Z&&>::value, "");
|
||||
static_assert(std::is_same<decltype(f(std::declval<X&>())),
|
||||
std::false_type>::value, "");
|
||||
static_assert(std::is_same<decltype(f(std::declval<X&&>())),
|
||||
std::false_type>::value, "");
|
||||
static_assert(std::is_same<decltype(f(std::declval<Y&>())),
|
||||
std::true_type>::value, "");
|
||||
static_assert(std::is_same<decltype(f(std::declval<Y&&>())),
|
||||
std::true_type>::value, "");
|
||||
static_assert(std::is_same<decltype(f(std::declval<Z&>())),
|
||||
std::false_type>::value, "");
|
||||
static_assert(std::is_same<decltype(f(std::declval<Z&&>())),
|
||||
std::false_type>::value, "");
|
||||
static_assert(std::is_same<decltype(g(std::declval<X&>())),
|
||||
std::false_type>::value, "");
|
||||
static_assert(std::is_same<decltype(g(std::declval<X&&>())),
|
||||
std::false_type>::value, "");
|
||||
static_assert(std::is_same<decltype(g(std::declval<Y&>())),
|
||||
std::true_type>::value, "");
|
||||
static_assert(std::is_same<decltype(g(std::declval<Y&&>())),
|
||||
std::true_type>::value, "");
|
||||
static_assert(std::is_same<decltype(g(std::declval<Z&>())),
|
||||
std::false_type>::value, "");
|
||||
static_assert(std::is_same<decltype(g(std::declval<Z&&>())),
|
||||
std::false_type>::value, "");
|
||||
static_assert(!is_insertable<std::wostream&, X&>::value, "");
|
||||
static_assert(!is_insertable<std::wostream&&, X&>::value, "");
|
||||
static_assert(!is_insertable<std::wostream&, X&&>::value, "");
|
||||
static_assert(!is_insertable<std::wostream&&, X&&>::value, "");
|
||||
static_assert(is_insertable<std::wostream&, Y&>::value, "");
|
||||
static_assert(is_insertable<std::wostream&&, Y&&>::value, "");
|
||||
static_assert(is_insertable<std::wostream&, Y&>::value, "");
|
||||
static_assert(is_insertable<std::wostream&&, Y&&>::value, "");
|
||||
static_assert(!is_insertable<std::wostream&, Z&>::value, "");
|
||||
static_assert(!is_insertable<std::wostream&&, Z&>::value, "");
|
||||
static_assert(!is_insertable<std::wostream&, Z&&>::value, "");
|
||||
static_assert(!is_insertable<std::wostream&&, Z&&>::value, "");
|
||||
}
|
||||
|
||||
int main()
|
||||
|
@ -33,3 +33,4 @@ void foo(std::iostream& s) {
|
||||
s >> p; // { dg-error "no match" }
|
||||
}
|
||||
// { dg-prune-output "no type .*enable_if" }
|
||||
// { dg-prune-output "no matching function for call to '__rval_streamable" }
|
||||
|
@ -58,16 +58,7 @@ struct X { };
|
||||
std::ostream& operator<<(std::ostream& os, const X&) { return os; }
|
||||
std::istream& operator>>(std::istream& is, X&&) { return is; }
|
||||
|
||||
struct O : std::ostream { };
|
||||
|
||||
void operator<<(O&, X) = delete;
|
||||
|
||||
struct I : std::istream { };
|
||||
|
||||
void operator>>(I&, X) = delete;
|
||||
|
||||
// PR libstdc++/65543
|
||||
// PR libstdc++/80675
|
||||
// PR libstdc++/80940
|
||||
int main()
|
||||
{
|
||||
@ -82,6 +73,4 @@ int main()
|
||||
MyStream2 stream2{};
|
||||
stream2 << "aaa";
|
||||
stream2 >> msi;
|
||||
O{} << X{};
|
||||
I{} >> X{};
|
||||
}
|
||||
|
@ -25,8 +25,6 @@ void
|
||||
test01()
|
||||
{
|
||||
int i = 1742;
|
||||
// This usage isn't supported by the current draft.
|
||||
// std::string result = (std::ostringstream() << i).str();
|
||||
std::ostringstream() << i;
|
||||
std::string result ("1742");
|
||||
int i2;
|
||||
@ -45,10 +43,10 @@ test02()
|
||||
{
|
||||
X x;
|
||||
std::istringstream is;
|
||||
auto& ref1 = (std::move(is) >> x);
|
||||
auto&& ref1 = (std::move(is) >> x);
|
||||
VERIFY( &ref1 == &is );
|
||||
VERIFY( x.as_rvalue == false );
|
||||
auto& ref2 = (std::move(is) >> std::move(x));
|
||||
auto&& ref2 = (std::move(is) >> std::move(x));
|
||||
VERIFY( &ref2 == &is );
|
||||
VERIFY( x.as_rvalue == true );
|
||||
|
||||
@ -57,6 +55,71 @@ test02()
|
||||
std::istringstream("x") >> &arr[0];
|
||||
#endif
|
||||
std::istringstream("x") >> arr;
|
||||
VERIFY( std::string(arr) == "x" );
|
||||
}
|
||||
|
||||
// LWG 1203 More useful rvalue stream insertion
|
||||
void
|
||||
test03()
|
||||
{
|
||||
int i = 1203;
|
||||
std::string result = (std::ostringstream() << "i = " << i).str();
|
||||
VERIFY( result == "i = 1203" );
|
||||
|
||||
std::ostringstream os;
|
||||
std::ostringstream&& ros = std::move(os) << result;
|
||||
VERIFY( &ros == &os );
|
||||
VERIFY( ros.str() == result );
|
||||
|
||||
std::stringstream ss;
|
||||
std::stringstream&& rss = std::move(ss) << result;
|
||||
VERIFY( &rss == &ss );
|
||||
VERIFY( rss.str() == result );
|
||||
|
||||
std::istringstream is("first second third");
|
||||
std::istringstream&& ris = std::move(is) >> result;
|
||||
VERIFY( &ris == &is );
|
||||
VERIFY( result == "first" );
|
||||
|
||||
std::stringstream ss2("fourth fifth sixth");
|
||||
std::stringstream&& rss2 = std::move(ss2) >> result;
|
||||
VERIFY( &rss2 == &ss2 );
|
||||
VERIFY( result == "fourth" );
|
||||
}
|
||||
|
||||
struct A { friend void operator<<(std::ios_base&, A) { } };
|
||||
|
||||
struct O : private std::ios_base { friend void operator<<(O&, int) { } };
|
||||
|
||||
template<typename Ostream, typename T, typename = void>
|
||||
struct is_insertable
|
||||
: std::false_type
|
||||
{ };
|
||||
|
||||
template<typename> using void_t = void;
|
||||
|
||||
template<typename Ostream, typename T>
|
||||
using insert_result
|
||||
= decltype(std::declval<Ostream>() << std::declval<const T&>());
|
||||
|
||||
template<typename Ostream, typename T>
|
||||
struct is_insertable<Ostream, T, void_t<insert_result<Ostream, T>>>
|
||||
: std::true_type
|
||||
{ };
|
||||
|
||||
// LWG 1203 negative tests
|
||||
void
|
||||
test04()
|
||||
{
|
||||
static_assert( is_insertable<std::ios_base&, A>::value,
|
||||
"valid using the friend operator<<" );
|
||||
static_assert( !is_insertable<std::ios_base&&, A>::value,
|
||||
"ill-formed because ios_base is not derived from ios_base" );
|
||||
|
||||
static_assert( is_insertable<O&, int>::value,
|
||||
"valid using the friend operator<<" );
|
||||
static_assert( !is_insertable<O&&, int>::value,
|
||||
"ill-formed because O is not publicly derived from ios_base" );
|
||||
}
|
||||
|
||||
int
|
||||
@ -64,5 +127,6 @@ main()
|
||||
{
|
||||
test01();
|
||||
test02();
|
||||
return 0;
|
||||
test03();
|
||||
test04();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user