The file system library has a very clear separation between the filesystem::path type, which represents the abstract path name (which is not even the name of an existing file) and operations that access the real physical file system, i.e. read + write data to discs.
You even pointed out an explanation for this:
The design rule is that purely lexical operations are provided as member functions of the class, and operations performed by the operating system are provided as free functions.
That's why.
Theoretically, you can use filesystem::path on a filesystem::path system. The path class simply contains a string of characters and allows you to manipulate this string, convert between character sets and use some rules that define the structure of file names and path names in the host OS. For example, he knows that directory names are split / on POSIX systems and \ on Windows. Manipulating the string contained in path is a “lexical operation” because it simply performs string manipulations.
Functions that are not members, which are known as file system operations, are completely different. They do not just work with the abstract path object, which is just a string of characters, they perform valid I / O operations that access the file system ( stat system calls, open , readdir , etc.), These operations take the path argument, which names files or directories to work with, and then they access real files or directories. They do not just manipulate strings in memory.
These operations depend on the API provided by the OS for accessing files, and depend on hardware that may not fully work in string manipulations. Disks may be full or may be disconnected before the operation is completed or may have hardware failures.
Looking at this, of course, file_size not a member of path , because it has nothing to do with the path itself. A path is simply a representation of the file name, not the actual file. The file_size function file_size for a physical file with the given name and tries to read its size. This is not a file name property, it is a permanent file property in the file system. Something that exists completely separate from the character string in memory that contains the file name.
In other words, I can have a path object that contains complete nonsense, such as filesystem::path p("hgkugkkgkuegakugnkunfkw") , and that is fine. I can add to this path or ask if it has a root directory, etc. But I can not read the size of such a file if it does not exist. I have a path to files that exist, but I do not have access to access, for example filesystem::path p("/root/secret_admin_files.txt"); , and that's fine too, because it's just a string of characters. When I try to access something in this place using the file system functions, I get an "access denied" error.
Because path member functions never touch the file system, they will never be able to exit due to permissions or nonexistent files. This is a useful guarantee.
You can observe a similar model with iterators, where at present we can (should?) Start (it) instead of it.begin (), but here I think that the rationale should have been more compatible with the -modulation of the following (this ) etc.
No, this was due to the fact that it works equally well with arrays (which cannot have member functions) and class types. If you know that the range you are dealing with is a container, not an array, then you can use x.begin() , but if you write general code and don’t know if it will be a container or an array, then std::begin(x) works in both cases.
The reasons for both of these things (file system design and out-of-band access functions) are not some preferences against OO, they have much more reasonable practical reasons. It would be a bad design based on any of them, because it feels better for those who love OO, or feels better for people who don't like OO.
In addition, there are things that you cannot do when all member functions are:
struct ConvertibleToPath { operator const std::filesystem::path& () const;
But if file_size was a member of path :
c.file_size();