This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of NAD status.

### 421. is `basic_streambuf` copy-constructible?

Section: 29.6.3.2 [streambuf.cons] Status: NAD Submitter: Martin Sebor Opened: 2003-09-18 Last modified: 2016-01-28

Priority: Not Prioritized

View all other issues in [streambuf.cons].

View all issues with NAD status.

Discussion:

The reflector thread starting with c++std-lib-11346 notes that the class template `basic_streambuf`, along with `basic_stringbuf` and `basic_filebuf`, is copy-constructible but that the semantics of the copy constructors are not defined anywhere. Further, different implementations behave differently in this respect: some prevent copy construction of objects of these types by declaring their copy ctors and assignment operators private, others exhibit undefined behavior, while others still give these operations well-defined semantics.

Note that this problem doesn't seem to be isolated to just the three types mentioned above. A number of other types in the library section of the standard provide a compiler-generated copy ctor and assignment operator yet fail to specify their semantics. It's believed that the only types for which this is actually a problem (i.e. types where the compiler-generated default may be inappropriate and may not have been intended) are locale facets. See issue 439.

[ 2009-07 Frankfurt ]

> NAD. Option B is already in the Working Draft.

Proposed resolution:

27.5.2 [lib.streambuf]: Add into the synopsis, public section, just above the destructor declaration:

>     basic_streambuf(const basic_streambuf& sb);
>     basic_streambuf& operator=(const basic_streambuf& sb);

Insert after 27.5.2.1, paragraph 2:

>     basic_streambuf(const basic_streambuf& sb);

> Constructs a copy of `sb`.

> Postcondtions:

>                     eback() == sb.eback()
>                     gptr()  == sb.gptr()
>                     egptr() == sb.egptr()
>                     pbase() == sb.pbase()
>                     pptr()  == sb.pptr()
>                     epptr() == sb.epptr()
>                     getloc() == sb.getloc()

>     basic_streambuf& operator=(const basic_streambuf& sb);

> Assigns the data members of `sb` to this.

> Postcondtions:

>                     eback() == sb.eback()
>                     gptr()  == sb.gptr()
>                     egptr() == sb.egptr()
>                     pbase() == sb.pbase()
>                     pptr()  == sb.pptr()
>                     epptr() == sb.epptr()
>                     getloc() == sb.getloc()

> Returns: *this.

27.7.1 [lib.stringbuf]:

Option A:

> Insert into the `basic_stringbuf` synopsis in the private section:

>     basic_stringbuf(const basic_stringbuf&);             // not defined
>     basic_stringbuf& operator=(const basic_stringbuf&);  // not defined

Option B:

> Insert into the `basic_stringbuf` synopsis in the public section:

>     basic_stringbuf(const basic_stringbuf& sb);
>     basic_stringbuf& operator=(const basic_stringbuf& sb);

> 27.7.1.1, insert after paragraph 4:

>     basic_stringbuf(const basic_stringbuf& sb);

> Constructs an independent copy of `sb` as if with `sb.str()`, and with the openmode that `sb` was constructed with.

> Postcondtions:

>                    str() == sb.str()
>                    gptr()  - eback() == sb.gptr()  - sb.eback()
>                    egptr() - eback() == sb.egptr() - sb.eback()
>                    pptr()  - pbase() == sb.pptr()  - sb.pbase()
>                    getloc() == sb.getloc()

> Note: The only requirement on `epptr()` is that it point beyond the initialized range if an output sequence exists. There is no requirement that `epptr() - pbase() == sb.epptr() - sb.pbase()`.

>     basic_stringbuf& operator=(const basic_stringbuf& sb);

> After assignment the `basic_stringbuf` has the same state as if it were initially copy constructed from `sb`, except that the `basic_stringbuf` is allowed to retain any excess capacity it might have, which may in turn effect the value of `epptr()`.

27.8.1.1 [lib.filebuf]

Insert at the bottom of the `basic_filebuf` synopsis:

>     private:
>       basic_filebuf(const basic_filebuf&);             // not defined
>       basic_filebuf& operator=(const basic_filebuf&);  // not defined

[Kona: this is an issue for `basic_streambuf` itself and for its derived classes. We are leaning toward allowing `basic_streambuf` to be copyable, and specifying its precise semantics. (Probably the obvious: copying the buffer pointers.) We are less sure whether the `streambuf` derived classes should be copyable. Howard will write up a proposal.]

[Sydney: Dietmar presented a new argument against `basic_streambuf` being copyable: it can lead to an encapsulation violation. `filebuf` inherits from `streambuf`. Now suppose you inherit a `my_hijacking_buf` from `streambuf`. You can copy the `streambuf` portion of a `filebuf` to a `my_hijacking_buf`, giving you access to the pointers into the `filebuf`'s internal buffer. Perhaps not a very strong argument, but it was strong enough to make people nervous. There was weak preference for having `streambuf` not be copyable. There was weak preference for having `stringbuf` not be copyable even if `streambuf` is. Move this issue to open for now. ]

[ 2007-01-12, Howard: Rvalue Reference Recommendations for Chapter 27 recommends protected copy constructor and assignment for `basic_streambuf` with the same semantics as would be generated by the compiler. These members aid in derived classes implementing move semantics. A protected copy constructor and copy assignment operator do not expose encapsulation more so than it is today as each data member of a `basic_streambuf` is already both readable and writable by derived classes via various get/set protected member functions (`eback()`, `setp()`, etc.). Rather a protected copy constructor and copy assignment operator simply make the job of derived classes implementing move semantics less tedious and error prone. ]

Rationale:

27.5.2 [lib.streambuf]: The proposed `basic_streambuf` copy constructor and assignment operator are the same as currently implied by the lack of declarations: public and simply copies the data members. This resolution is not a change but a clarification of the current standard.

27.7.1 [lib.stringbuf]: There are two reasonable options: A) Make `basic_stringbuf` not copyable. This is likely the status-quo of current implementations. B) Reasonable copy semantics of `basic_stringbuf` can be defined and implemented. A copyable `basic_streambuf` is arguably more useful than a non-copyable one. This should be considered as new functionality and not the fixing of a defect. If option B is chosen, ramifications from issue 432 are taken into account.

27.8.1.1 [lib.filebuf]: There are no reasonable copy semantics for `basic_filebuf`.