This (pretty old) article on iostreams and streambuf argues, that the following code is OK:
class DerivedStreamBuf : public std::streambuf {
// ...
};
class DerivedOutputStream : public std::ostream {
public:
DerivedOutputStream():
std::ios(0), std::ostream(&dsb) {} //1
// ...
private:
DerivedStreamBuf dsb;
// ...
};
It might be problematic, because when ostream
is constructed dsb
isn't yet initialized and thus UB could be the effect. For the destructor it might be the other way around: dsb
is already destructed and could be used in the destructor of ostream
.
However, the article argues, "the C++ standard mandates that no parent class constructors or destructors (ios, istream, or ostream) access the stream buffer".
While the case of destructor is straight forward, e.g. for ~ostream
:
virtual ~basic_ostream(); Remarks: Does not perform any operations on rdbuf().
It is less clear for constructor:
explicit basic_ostream(basic_streambuf<charT, traits>* sb); Effects: Initializes the base class subobject with basic_ios<charT, traits>::init(sb) ([basic.ios.cons]).
Does it means this is the only possible effect, does this standard-formulation not allow for other effects, which are implementation details of std::ostream
and could potentially access non-initialized dsb
?
My question: Does that standard guarantees, that the above code is portable, i.e. works for all standard-compliant implementations of std::ostream
?
Does that standard guarantees, that the above code is portable, i.e. works for all standard-compliant implementations of
std::ostream
?
Yes.
At the point of calling std::ostream(&dsb)
the dsb
member of DerivedOutputStream
has been allocated but not initialized. Meaning, we may take its address by we may not yet read its (uninitialized) value.
As linked in the question, the [ostream.cons] specifies the constructors of std::basic_ostream
, and, particularly, the one chosen in you example is [ostream.cons]/1
explicit basic_ostream(basic_streambuf<charT, traits>* sb);
Effects: Initializes the base class subobject with
basic_ios<charT, traits>::init(sb)
([basic.ios.cons]).Postconditions:
rdbuf() == sb
.
[tab:basic.ios.cons] describe the effects of invoking void init(basic_streambuf<charT, traits>* sb)
, one which is particularly the post-condition described in the constructor above:
rdbuf() == sb
and the rest of which does performs no read access to the underlying buffer to which sb
points.
Now, rdbuf()
is just a pointer, so this means the sb
pointer passed to init by value (the pointer, that is) will be use to initialize rdbuf()
. However, there is no side effect of reading the underlying buffer that sb
points to, at this point.
[...] not allow for other effects, ...?
Indeed, an implementation for, particularly, the void init(basic_streambuf<charT, traits>*)
function need to respect and not expand the well-specified effects of [tab:basic.ios.cons].
This (pretty old) article ...
Note that the state of [ostream.cons] has been basically unchanged since the dawn of C++ time; we may e.g. have a look at [lib.input.output] from the September 1994 C++ Working Paper
27.2.4.1.1 basic_ostream constructor [lib.basic.ostream.sb.cons]
basic_ostream(basic_streambuf<charT,baggage>* sb);
1 Constructs an object of class basic_ostream, assigning initial values to the base class by calling
basic_ios<charT,baggage>::init(sb)
.
and
27.1.3.1.34 basic_ios::init [lib.basic.ios::init]
void init(basic_streambuf<charT,baggage>* sb_arg);
1 The postconditions of this function are indicated in Table 8:
Table 8--init effects +----------------------------------+ |Element Value | +----------------------------------+ |sb sb_arg | |tiestr a null pointer | |state goodbit if sb_arg is | | not a null pointer, | | otherwise badbit. | |except goodbit | |fmtfl skipws | dec | |wide zero | |prec 6 | |fillch the space character | |loc new locale(), which | | means the default value | | is the current global | | locale;9) | |iarray a null pointer | |parray a null pointer | +----------------------------------+
which describe the same delegation via an init()
call with well-defined effects.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With