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

### 2989. `path`'s stream insertion operator lets you insert everything under the sun

Section: 31.12.6.7 [fs.path.io] Status: C++20 Submitter: Billy O'Neal III Opened: 2017-06-27 Last modified: 2021-02-25

Priority: 2

View all issues with C++20 status.

Discussion:

The rules for converting a `path` to a narrow character sequence aren't necessarily the same as that iostreams should use. Note that this program creates a temporary path and stream inserts that, which likely destroys information.

>     #include <iostream>
>     #include <filesystem>
>     #include <string>

>     void foo() {
>       using namespace std;
>       using namespace std::experimental::filesystem::v1;
>       wstring val(L"abc");
>       std::cout << val;
>     }

Using the godbolt online compiler we get:

>     foo PROC
>       sub      rsp, 104   ; 00000068H
>       lea      rdx, OFFSET FLAT:$SG44895
>       lea      rcx, QWORD PTR val$[rsp]
>       call     std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t>
>                >::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t> >
>       lea      rdx, QWORD PTR val$[rsp]
>       lea      rcx, QWORD PTR $T1[rsp]
>       call     ??$?0_WU?$char_traits@_W@std@@V?$allocator@_W@1@@path@v1@filesystem@experimental@std@@QEAA@AEBV?$basic_string@_WU?$char_traits@_W@std@@V?$allocator@_W@2@@4@@Z
>       lea      rdx, QWORD PTR $T1[rsp]
>       lea      rcx, OFFSET FLAT:std::cout
>       call     std::experimental::filesystem::v1::operator<<<char,std::char_traits<char> >
>       lea      rcx, QWORD PTR $T1[rsp]
>       call     std::experimental::filesystem::v1::path::~path
>       lea      rcx, QWORD PTR val$[rsp]
>       call     std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t>
>                >::~basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t> >
>       add      rsp, 104   ; 00000068H
>       ret      0
>     foo ENDP

This should either be disabled with a SFINAE constraint, use the `auto_ptr` user-defined conversion trick, or the stream insertion operators should be made "hidden friends" to prevent conversions to `path` from being considered here.

[2017-07 Toronto Monday issue prioritization]

Priority 2

[2017-07 Toronto Saturday afternoon]

LWG confirmed they want the hidden friend solution, Billy O'Neal to provide wording.

[2018-1-26 issues processing telecon]

Status to 'Tentatively Ready'

[2018-3-17 Adopted in Jacksonville]

Proposed resolution:

This resolution is relative to N4659.

1. Edit 31.12.4 [fs.filesystem.syn], header `<filesystem>` synopsis, as indicated:

   >     // 31.12.6.7 [fs.path.io], path inserter and extractor
   >     template <class charT, class traits>
   >       basic_ostream<charT, traits>&
   >         operator<<(basic_ostream<charT, traits>& os, const path& p);
   >     template <class charT, class traits>
   >       basic_istream<charT, traits>&
   >         operator>>(basic_istream<charT, traits>& is, path& p);

2. Edit 31.12.6.7 [fs.path.io] as indicated:

   > [Drafting note: The project editor is kindly asked to consider to move sub-clause 31.12.6.7 [fs.path.io] before sub-clause 31.12.6.8 [fs.path.nonmember] (as a peer of it) — end drafting note]

   >     template <class charT, class traits>
   >       friend basic_ostream<charT, traits>&
   >         operator<<(basic_ostream<charT, traits>& os, const path& p);

   >     […]

   >     template <class charT, class traits>
   >       friend basic_istream<charT, traits>&
   >         operator>>(basic_istream<charT, traits>& is, path& p);

3. Edit 31.12.6 [fs.class.path] p2, class `path` synopsis, as indicated:

   >     namespace std::filesystem {
   >       class path {
   >       public:
   >         […]
   >         iterator begin() const;
   >         iterator end() const;

   >         // 31.12.6.7 [fs.path.io] path inserter and extractor
   >         template <class charT, class traits>
   >           friend basic_ostream<charT, traits>&
   >             operator<<(basic_ostream<charT, traits>& os, const path& p);
   >         template <class charT, class traits>
   >           friend basic_istream<charT, traits>&
   >             operator>>(basic_istream<charT, traits>& is, path& p);
   >       };
   >     }