std::fstream
& co. should be constructible from string_view
Section: 31.10.1 [fstream.syn] Status: C++23 Submitter: Jonathan Wakely Opened: 2020-04-15 Last modified: 2023-11-22
Priority: 3
View all issues with C++23 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.
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.
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 takefilesystem::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 astring_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.
Modify 31.10.4 [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(constfilesystem::pathT& s, ios_base::openmode mode = ios_base::in); […]
Modify 31.10.4.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(constfilesystem::pathT& s, ios_base::openmode mode = ios_base::in);-?- Constraints:
-3- Effects: Equivalent to:is_same_v<T, filesystem::path>
istrue
.basic_ifstream(s.c_str(), mode)
.
Modify 31.10.5 [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(constfilesystem::pathT& s, ios_base::openmode mode = ios_base::out); […]
Modify 31.10.5.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(constfilesystem::pathT& s, ios_base::openmode mode = ios_base::out);-?- Constraints:
-3- Effects: Equivalent to:is_same_v<T, filesystem::path>
istrue
.basic_ofstream(s.c_str(), mode)
.
Modify 31.10.6 [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( constfilesystem::pathT& s, ios_base::openmode mode = ios_base::in | ios_base::out); […]
Modify 31.10.6.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( constfilesystem::pathT& s, ios_base::openmode mode = ios_base::in | ios_base::out);-?- Constraints:
-3- Effects: Equivalent to:is_same_v<T, filesystem::path>
istrue
.basic_fstream(s.c_str(), mode)
.