I'm working on a file traversal project, and I want to keep track of the current item that's being examined, so I have an external filesystem::path CurrentItem
value that I change on each level/step of traversal to keep up to date.
I'm using a recursive function that takes a const filesystem::path&
to traverse through the files, the const path
reference rather than a path
itself so I can avoid the string copy overheads. I know in the scale of things, it's probably not the most heavy of operations in this program, but it's what I chose to do.
When traversing beyond the entry-depth of the loop, when I update the CurrentItem
path with the loop's directory_entry
's path, the function parameter path
also somehow gets set. It's baffling to me especially considering that adding a watch it showed me that even the underlying wstring
is marked as const
, so I'm at a loss.
Bottom line, I want to know if I somehow did something wrong enough to break const
, or if it's actually a bug that I just need to work around. I can just create a secondary path
that I assign the input value to, and that doesn't have the same problem as the const
parameter, or I can change the parameter to a plain const path
, but both of these solutions reintroduce the additional unnecessary copies that I wanted to avoid.
I separated the code from my project in a completely clean environment, and it exhibits the same exact behavior.
I reproduced all external variables as they would be, minus class encapsulation. I'm running in VS19 on windows 10, tested on both 32 and 64 bit and the same happens. Here it is:
#include <iostream>
#include <filesystem>
using namespace std::filesystem;
std::error_code ec;
path StartDirectory = L"D:/Camera Import/Animals/RAW";
path CurrentItem;
bool Recursive = true;
bool DoFiles = false;
bool DoFolders = true;
void HandleFilesInDirectory(const path& thisPath);
int main()
{
std::cout << "Hello World!\n";
StartDirectory.make_preferred();
CurrentItem = StartDirectory;
if (is_directory(StartDirectory, ec))
{
HandleFilesInDirectory(StartDirectory);
}
}
void HandleFilesInDirectory(const path& thisPath)
{
for (const directory_entry& entry : directory_iterator(thisPath))
{
auto type = entry.status().type();
// This assignment operation causes the const path& thisPath to be reassigned somehow,
// despite it being const and also being unrelated to the items in the assignment
CurrentItem = entry.path();
if (entry.is_directory(ec))
{
if (Recursive)
{
HandleFilesInDirectory(CurrentItem);
}
if (DoFolders)
{
// Do project-specific operations
}
}
else if (DoFiles && entry.is_regular_file(ec))
{
// Do project-specific operations
}
}
CurrentItem = thisPath;
if (!thisPath.compare(StartDirectory))
{
// Do project-specific operations
}
}
This is what happens when you use modifiable global variables.
The object referenced by thisPath
cannot be modified by the thisPath
variable, but if there is another way to access that object, then it can take on different values. For example, if it is a reference to a global variable, if you modify that global variable, then the referenced object has been changed.
Of course, your HandleFilesInDirectory
function calls itself with CurrentItem
. This means that the recursive call to this function will be given a reference to a modifiable global variable. A global variable you then modify within the function.
const&
is only a safeguard to modifications through that variable. If the object is modifiable in some other way, the const&
will see those modifications.