Section: 31.7.6.6 [ostream.rvalue], 31.7.5.6 [istream.rvalue] Status: C++20 Submitter: Howard Hinnant Opened: 2009-09-06 Last modified: 2021-02-25
Priority: 2
View all other issues in [ostream.rvalue].
View all issues with C++20 status.
Discussion:
31.7.6.6 [ostream.rvalue] was created to preserve the ability to insert into (and extract from 31.7.5.6 [istream.rvalue]) rvalue streams:
template <class charT, class traits, class T> basic_ostream<charT, traits>& operator<<(basic_ostream<charT, traits>&& os, const T& x);1 Effects:
os << x
2 Returns:
os
This is good as it allows code that wants to (for example) open, write to, and
close an ofstream
all in one statement:
std::ofstream("log file") << "Some message\n";
However, I think we can easily make this "rvalue stream helper" even easier to use. Consider trying to quickly create a formatted string. With the current spec you have to write:
std::string s = static_cast<std::ostringstream&>(std::ostringstream() << "i = " << i).str();
This will store "i = 10
" (for example) in the string s
. Note
the need to cast the stream back to ostringstream&
prior to using
the member .str()
. This is necessary because the inserter has cast
the ostringstream
down to a more generic ostream
during the
insertion process.
I believe we can re-specify the rvalue-inserter so that this cast is unnecessary. Thus our customer now has to only type:
std::string s = (std::ostringstream() << "i = " << i).str();
This is accomplished by having the rvalue stream inserter return an rvalue of
the same type, instead of casting it down to the base class. This is done by
making the stream generic, and constraining it to be an rvalue of a type derived
from ios_base
.
The same argument and solution also applies to the inserter. This code has been implemented and tested.
[ 2009 Santa Cruz: ]
NAD Future. No concensus for change.
[LEWG Kona 2017]
Recommend Open: Design looks right.
[ 2018-05-25, Billy O'Neal requests this issue be reopened and provides P/R rebased against N4750 ]
Billy O'Neal requests this issue be reopened, as changing operator>> and operator<< to use perfect forwarding as described here is necessary to implement LWG 2534 which was accepted. Moreover, this P/R also resolves LWG 2498.
Previous resolution [SUPERSEDED]:
Change 31.7.5.6 [istream.rvalue]:
template <classcharT, class traitsIstream, class T>basic_istream<charT, traits>&Istream&& operator>>(basic_istream<charT, traits>Istream&& is, T& x);1 Effects:
is >> x
2 Returns:
std::move(is)
3 Remarks: This signature shall participate in overload resolution if and only if
Istream
is not an lvalue reference type and is derived fromios_base
.Change 31.7.6.6 [ostream.rvalue]:
template <classcharT, class traitsOstream, class T>basic_ostream<charT, traits>&Ostream&& operator<<(basic_ostream<charT, traits>Ostream&& os, const T& x);1 Effects:
os << x
2 Returns:
std::move(os)
3 Remarks: This signature shall participate in overload resolution if and only if
Ostream
is not an lvalue reference type and is derived fromios_base
.
[2018-12-03, Ville comments]
The implementation in libstdc++ doesn't require derivation from ios_base
, it
requires convertibility to basic_istream/basic_ostream
. This has been found to be
important to avoid regressions with existing stream wrappers.
basic_istream/basic_ostream
specialization
the template parameter converts to. This was done in order to try and be closer to the earlier
specification's return type, which specified basic_ostream<charT, traits>&
and basic_istream<charT, traits>&
. So we detected convertibility to
(a type convertible to) those, and returned the result of that conversion. That doesn't seem to
be necessary, and probably bothers certain chaining cases. Based on recent experiments, it seems
that this return-type dance (as opposed to just returning what the p/r suggests) is unnecessary,
and doesn't trigger any regressions.
[2019-01-20 Reflector prioritization]
Set Priority to 2
Previous resolution [SUPERSEDED]:
This resolution is relative to N4750.
Change 31.7.5.6 [istream.rvalue] as follows:
template <classcharT, class traitsIstream, class T>basic_istream<charT, traits>&Istream&& operator>>(basic_istream<charT, traits>Istream&& is, T&& x);-1- Effects: Equivalent to:
is >> std::forward<T>(x)
return std::move(is);-2- Remarks: This function shall not participate in overload resolution unless the expression
is >> std::forward<T>(x)
is well-formed,Istream
is not an lvalue reference type, andIstream
is derived fromios_base
.Change 31.7.6.6 [ostream.rvalue]:
template <classcharT, class traitsOstream, class T>basic_ostream<charT, traits>&Ostream&& operator<<(basic_ostream<charT, traits>Ostream&& os, const T& x);-1- Effects: As if by:
os << x;
-2- Returns:
std::move(os)
-3- Remarks: This signature shall not participate in overload resolution unless the expression
os << x
is well-formed,Ostream
is not an lvalue reference type, andOstream
is derived fromios_base
.
[2019-03-17; Daniel comments and provides updated wording]
After discussion with Ville it turns out that the "derived from ios_base
" approach works fine and
no breakages were found in regression tests. As result of that discussion the wording was rebased to the
most recent working draft and the "overload resolution participation" wording was replaced by a
corresponding Constraints: element.
[2020-02 Status to Immediate on Friday morning in Prague.]
Proposed resolution:
This wording is relative to N4810.
Change 31.7.5.6 [istream.rvalue] as follows:
template <classcharT, class traitsIstream, class T>basic_istream<charT, traits>&Istream&& operator>>(basic_istream<charT, traits>Istream&& is, T&& x);-?- Constraints: The expression
-1- Effects: Equivalent to:is >> std::forward<T>(x)
is well-formed andIstream
is publicly and unambiguously derived fromios_base
.is >> std::forward<T>(x)
; return std::move(is);
-2- Remarks: This function shall not participate in overload resolution unless the expressionis >> std::forward<T>(x)
is well-formed.
Change 31.7.6.6 [ostream.rvalue]:
template <classcharT, class traitsOstream, class T>basic_ostream<charT, traits>&Ostream&& operator<<(basic_ostream<charT, traits>Ostream&& os, const T& x);-?- Constraints: The expression
-1- Effects: As if by:os << x
is well-formed andOstream
is publicly and unambiguously derived fromios_base
.os << x;
-2- Returns:std::move(os)
.-3- Remarks: This signature shall not participate in overload resolution unless the expressionos << x
is well-formed.