6

Here is a reproducible example taken from question about using temporary stringstream object:

#include <sstream> #include <string> #include <iostream> using namespace std; std::string transform(std::string); int main() { int i{}; cout << transform( static_cast<stringstream &>(stringstream() << i).str() ); } 

When trying to compile with clang version 9.0.0 under MacOS High Sierra I got following error:

$ clang++ -std=c++11 x.cc -c x.cc:12:24: error: non-const lvalue reference to type 'basic_stringstream<...>' cannot bind to a temporary of type 'basic_stringstream<...>' cout << transform( static_cast<stringstream &>(stringstream() << i).str() ); ^ ~~~~~~~~~~~~~~~~~~~ 1 error generated. 

When g++ 9.2.0 is used on same machine (also on Linux) everything compiles fine.

Seems that changing cast from stringstream & to const stringstream & or to stringstream && solves problem.

The question is if this is compiler bug or maybe clang is more strict about some standard rules?

6
  • Cannot reproduce... Commented Dec 13, 2019 at 15:17
  • FWIW, if you just need a single thing converted, transform(std::to_string(i)) works and IMHO is easier to understand. Commented Dec 13, 2019 at 15:18
  • 1
    @MaxLanghof IIRC godbolt uses libstdc++ for clang instead of libc++. It reproduces on wandbox: wandbox.org/permlink/mwEqfxr8hW7uhx4w Commented Dec 13, 2019 at 15:20
  • @NathanOliver-ReinstateMonica True, with the stdlib=libc++ it fails: godbolt.org/z/4iMPgc. Commented Dec 13, 2019 at 15:22
  • 1
    not the problem but using namespace std; + std::string transform(std::string); will cause confusion sooner or later Commented Dec 13, 2019 at 15:28

2 Answers 2

2

Yes, I think that is a bug in libc++.

According to [ostream.rvalue] there is an overload:

template<class charT, class traits, class T> basic_ostream<charT, traits>& operator<<(basic_ostream<charT, traits>&& os, const T& x); 

But libc++ implements this similar to:

template <class _Stream, class _Tp> enable_if_t</*...*/, _Stream&&> operator<<(_Stream&& __os, const _Tp& __x) { // ... } 

The implementation makes this overload a better candidate than the in-class operator<< of ostream in overload resolution if a stream rvalue is used with <<, while the signature in the standard wouldn't be, and it also returns a rvalue reference, when it should return a lvalue reference. It also returns a reference of same type as was passed to it, while it should return a reference to its ostream base class, according to the standard quote.

The rvalue reference can not be bound to a non-const lvalue reference, hence the error.

The bug has already been reported here and there is an open LWG issue regarding the behavior here, which seems to suggest that in the future the standard might be adjusted to mandate libc++'s current behavior.

Sign up to request clarification or add additional context in comments.

1 Comment

So this would be a breaking change for all the code out there implementing the workaround in the question (this happens with fstreams too) ... I wonder if it is possible to return something that can be cast to lvalue reference and rvalue reference so that old code doesn't break
1

The answer to that is a different implementation of ostream insertion between libstdc++ and libc++.

The implementation is almost the same, but libc++ implementation has following code in it (https://github.com/llvm/llvm-project/blob/master/libcxx/include/ostream):

template <class _Stream, class _Tp> inline _LIBCPP_INLINE_VISIBILITY typename enable_if < !is_lvalue_reference<_Stream>::value && is_base_of<ios_base, _Stream>::value, _Stream&& >::type operator<<(_Stream&& __os, const _Tp& __x) { __os << __x; return _VSTD::move(__os); } 

This grabs the rvalue strstream you create in situ and returns an rvalue, to which lvalue reference can't bind.

On the other hand, libstdc++ implementation has following:

 template<typename _Ostream> using __rvalue_ostream_type = typename __is_convertible_to_basic_ostream< _Ostream>::__ostream_type; 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 operator<<(_Ostream&& __os, const _Tp& __x) { __rvalue_ostream_type<_Ostream> __ret_os = __os; __ret_os << __x; return __ret_os; 

Where __rvalue_ostream_type after some magic becomes not an r-value reference. Thus code compiles.

The above is a bug in libstdc++ and can be submitted as a bug report.

1 Comment

It does not seem that there is any point reporting it. It has already been reported and there is an open LWG issue regarding this behavior, see my answer.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.