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

### 3501. `basic_syncbuf`-related manipulators refer to some `Allocator` without defining it

Section: 31.7.6.5 [ostream.manip] Status: New Submitter: Jonathan Wakely Opened: 2020-11-15 Last modified: 2020-11-21

Priority: 3

View all other issues in [ostream.manip].

View all issues with New status.

Discussion:

From this editorial issue request:

The three `basic_syncbuf`-related manipulators `emit_on_flush`, `noemit_on_flush`, and `flush_emit` use in their Effects: elements the following wording:

> "If `os.rdbuf()` is a `basic_syncbuf<charT, traits, Allocator>*`, called `buf` for the purpose of exposition, calls […]

There are two problems with that wording (even when considering the helpful note following p8): First, the type `Allocator` is not defined elsewhere (e.g. it is not part of the function signature) and second, `os.rdbuf()` has type `basic_streambuf<charT, traits>*` and not any other type.

By introducing an expository `SYNCBUF` to detect `basic_syncbuf` during the work on the above mentioned editorial issue to solve these problems it turned out that the suggested wording fix would introduce an apparently normative change that the `syncbuf` type must not use a program-defined specialization.

[2020-11-21; Reflector prioritization]

Set priority to 3 during reflector discussions.

Proposed resolution:

This wording is relative to N4868.

> This proposed wording is known to be incorrect, but is nonetheless depicted to present the overall idea.

1. Modify 31.7.6.5 [ostream.manip] as indicated:

   > -1- Each instantiation of any of the function templates specified in this subclause is a designated addressable function (16.4.5.2.1 [namespace.std]).

   > -?- In this subclause, `SYNCBUF(p)` for a pointer `p` of type `B*` is determined as follows. If `*p` is a base class subobject of an object of type `S`, where `S` is a specialization generated from the `basic_syncbuf` primary template, and `is_convertible_v<S*, B*>` is `true`, then `SYNCBUF(p)` is `dynamic_cast<S*>(p)`. Otherwise, `SYNCBUF(p)` is `static_cast<void*>(nullptr)`. [Note ?: To work around the issue that the `Allocator` template argument of `S` cannot be deduced, implementations can introduce an intermediate base class to `basic_syncbuf` that manages its `emit_on_sync` flag. — end note]

   > […]

   >     template<class charT, class traits>
   >       basic_ostream<charT, traits>& emit_on_flush(basic_ostream<charT, traits>& os);

   > > Let `p` be `SYNCBUF(os.rdbuf())`.

   > > -8- Effects: If `pos.rdbuf()` is not null a `basic_syncbuf<charT, traits, Allocator>*`, called `buf` for the purpose of exposition, calls `pbuf->set_emit_on_sync(true)`. Otherwise this manipulator has no effect. [Note 1: To work around the issue that the Allocator template argument cannot be deduced, implementations can introduce an intermediate base class to `basic_syncbuf` that manages its `emit_on_sync` flag. — end note]

   > > -9- Returns: `os`.

   >     template<class charT, class traits>
   >       basic_ostream<charT, traits>& noemit_on_flush(basic_ostream<charT, traits>& os);

   > > Let `p` be `SYNCBUF(os.rdbuf())`.

   > > -10- Effects: If `pos.rdbuf()` is not null a `basic_syncbuf<charT, traits, Allocator>*`, called `buf` for the purpose of exposition, calls `pbuf->set_emit_on_sync(false)`. Otherwise this manipulator has no effect.

   > > -11- Returns: `os`.

   >     template<class charT, class traits>
   >       basic_ostream<charT, traits>& flush_emit(basic_ostream<charT, traits>& os);

   > > Let `p` be `SYNCBUF(os.rdbuf())`.

   > > -12- Effects: Calls `os.flush()`. Then, if `pos.rdbuf()` is not null a `basic_syncbuf<charT, traits, Allocator>*`, called `buf` for the purpose of exposition, calls `pbuf->emit()`.

   > > -13- Returns: `os`.