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

Section: 30.11.7.6.1 [fs.path.io] Status: New Submitter: Billy O'Neal III Opened: 2017-06-27 Last modified: 2017-07-16

Priority: 2

View all issues with New 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.

Proposed resolution:

This resolution is relative to N4659.

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

    // 30.11.7.6.1 [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 30.11.7.6.1 [fs.path.io] as indicated:

    [Drafting note: The project editor is kindly asked to consider to move sub-clause 30.11.7.6.1 [fs.path.io] before sub-clause 30.11.7.6 [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 30.11.7 [fs.class.path] p2, class path synopsis, as indicated:

    namespace std::filesystem {
      class path {
      public:
        […]
        iterator begin() const;
        iterator end() const;
    
        // 30.11.7.6.1 [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);
      };
    }