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

### 3430. `std::fstream` & co. should be constructible from `string_view`

Section: 31.10.1 [fstream.syn] Status: WP Submitter: Jonathan Wakely Opened: 2020-04-15 Last modified: 2021-06-07

Priority: 3

View all issues with WP status.

Discussion:

We have:

>     basic_fstream(const char*, openmode);
>     basic_fstream(const filesystem::path::value_type*, openmode); // wide systems only
>     basic_fstream(const string&, openmode);
>     basic_fstream(const filesystem::path&, openmode);

I think the omission of a `string_view` overload was intentional, because the underlying OS call (such as `fopen`) needs a NTBS. We wanted the allocation required to turn a `string_view` into an NTBS to be explicitly requested by the user. But then we added the `path` overload, which is callable with a `string_view`. Converting to a `path` is more expensive than converting to `std::string`, because a path has to at least construct a `basic_string`, and potentially also does an encoding conversion, parses the path, and potentially allocates a sequence of `path` objects for the path components.

This means the simpler, more obvious code is slower and uses more memory:

>     string_view sv = "foo.txt";
>     fstream f1(sv); // bad
>     fstream f2(string(sv)); // good

We should just allow passing a `string_view` directly, since it already compiles but doesn't do what anybody expects or wants.

Even with a `string_view` overload, passing types like `const char16_t*` or `u32string_view` will still implicitly convert to `filesystem::path`, but that seems reasonable. In those cases the encoding conversion is necessary. For Windows we support construction from `const wchar_t*` but not from `wstring` or `wstring_view`, which means those types will convert to `filesystem::path`. That seems suboptimal, so we might also want to add `wstring` and `wstring_view` overloads for "wide systems only", as per 31.10.1 [fstream.syn] p3.

Daniel:

LWG 2883 has a more general view on that but does not consider potential cost differences in the presence of `path` overloads (Which didn't exist at this point yet).

[2020-05-09; Reflector prioritization]

Set priority to 3 after reflector discussions.

[2020-08-10; Jonathan comments]

An alternative fix would be to retain the original design and not allow construction from a `string_view`. The `path` parameters could be changed to template parameters which are constrained to be exactly `path`, and not things like `string_view` which can convert to `path`.

[2020-08-21; Issue processing telecon: send to LEWG]

Just adding support for `string_view` doesn't prevent expensive conversions from other types that convert to `path`. Preference for avoiding all expensive implicit conversions to `path`, maybe via abbreviated function templates:

    basic_fstream(same_as<filesystem::path> auto const&, openmode);

It's possible `path_view` will provide a better option at some point.

It was noted that 2676 did intentionally allow conversions from "strings of character types `wchar_t`, `char16_t`, and `char32_t`". Those conversions don't need to be implicit for that to be supported.

[2020-09-11; Tomasz comments and provides wording]

During the LEWG 2020-08-24 telecon the LEWG provided following guidance on the issue:

> We took one poll (exact wording in the notes) to constrain the constructor which takes `filesystem::path` to only take `filesystem::path` and not things convertible to it, but only 9 out of 26 people present actually voted. Our interpretation: LWG should go ahead with making this change. There is still plenty of time for someone who hasn't yet commented on this to bring it up even if it is in a tentatively ready state. It would be nice to see a paper to address the problem of the templated path constructor, but no one has yet volunteered to do so. Note: the issue description is now a misnomer, as adding a `string_view` constructor is no longer being considered at this time.

The proposed P/R follows original LWG proposal and makes the `path` constructor of the `basic_*fstreams` "`explicit`". To adhere to current policy, we refrain from use of requires clauses and abbreviated function syntax, and introduce a Constraints element.

[2021-05-21; Reflector poll]

Set status to Tentatively Ready after seven votes in favour during reflector poll.

[2021-06-07 Approved at June 2021 virtual plenary. Status changed: Voting → WP.]

Proposed resolution:

This wording is relative to N4861.

1. Modify 31.10.3 [ifstream], class template `basic_ifstream` synopsis, as indicated:

   >     […]
   >     explicit basic_ifstream(const string& s,
   >                             ios_base::openmode mode = ios_base::in);
   >     template<class T>
   >     explicit basic_ifstream(const filesystem::pathT& s,
   >                             ios_base::openmode mode = ios_base::in);
   >     […]

2. Modify 31.10.3.2 [ifstream.cons] as indicated:

   >     explicit basic_ifstream(const string& s,
   >                             ios_base::openmode mode = ios_base::in);

   > > -?- Effects: Equivalent to: `basic_ifstream(s.c_str(), mode)`.

   >     template<class T>
   >     explicit basic_ifstream(const filesystem::pathT& s,
   >                             ios_base::openmode mode = ios_base::in);

   > > -?- Constraints: `is_same_v<T, filesystem::path>` is `true`.

   > > -3- Effects: Equivalent to: `basic_ifstream(s.c_str(), mode)`.

3. Modify 31.10.4 [ofstream], class template `basic_ofstream` synopsis, as indicated:

   >     […]
   >     explicit basic_ofstream(const string& s,
   >                             ios_base::openmode mode = ios_base::out);
   >     template<class T>
   >     explicit basic_ofstream(const filesystem::pathT& s,
   >                             ios_base::openmode mode = ios_base::out);
   >     […]

4. Modify 31.10.4.2 [ofstream.cons] as indicated:

   >     explicit basic_ofstream(const string& s,
   >                             ios_base::openmode mode = ios_base::out);

   > > -?- Effects: Equivalent to: `basic_ofstream(s.c_str(), mode)`.

   >     template<class T>
   >     explicit basic_ofstream(const filesystem::pathT& s,
   >                             ios_base::openmode mode = ios_base::out);

   > > -?- Constraints: `is_same_v<T, filesystem::path>` is `true`.

   > > -3- Effects: Equivalent to: `basic_ofstream(s.c_str(), mode)`.

5. Modify 31.10.5 [fstream], class template `basic_fstream` synopsis, as indicated:

   >     […]
   >     explicit basic_fstream(
   >       const string& s,
   >       ios_base::openmode mode = ios_base::in | ios_base::out);
   >     template<class T>
   >     explicit basic_fstream(
   >       const filesystem::pathT& s,
   >       ios_base::openmode mode = ios_base::in | ios_base::out);
   >     […]

6. Modify 31.10.5.2 [fstream.cons] as indicated:

   >     explicit basic_fstream(
   >       const string& s,
   >       ios_base::openmode mode = ios_base::in | ios_base::out);

   > > -?- Effects: Equivalent to: `basic_fstream(s.c_str(), mode)`.

   >     template<class T>
   >     explicit basic_fstream(
   >       const filesystem::pathT& s,
   >       ios_base::openmode mode = ios_base::in | ios_base::out);

   > > -?- Constraints: `is_same_v<T, filesystem::path>` is `true`.

   > > -3- Effects: Equivalent to: `basic_fstream(s.c_str(), mode)`.