我的问题集中在 listDirs 函数中的 for 循环,我在其中启动异步任务。我通过引用
std::async
传递路径,然后在单独的线程中调用 listDir 函数。
我知道,一旦 for 循环移动到下一次迭代,路径变量(对路径向量中的
std::filesystem::path
实例的 const 引用)就会超出范围。但是,listDir 函数的参数是一个引用,应该绑定到路径。
我的理解是,即使路径超出了 listDirs 函数的范围,路径向量中的实际
std::filesystem::path
实例在 listDirs 函数的整个持续时间内仍然存在,因为我们经过 std::ref
。但我不确定这个理解是否正确。
有人可以澄清这是如何工作的吗?具体来说:
std::async
中的std::ref是否确保即使path超出listDirs函数的范围,listDir也能获得有效的引用?
在这种情况下是否存在悬空引用的风险?
#include <filesystem>
using Iterator = std::filesystem::directory_iterator;
// The caller of this function is the thread runtime
std::vector<std::string> listDir(const std::filesystem::path& directory)
{
std::vector<std::string> files;
for (Iterator it(directory); it != Iterator(); ++it)
{
if (it->is_regular_file())
{
files.emplace_back(it->path().filename().string());
}
}
// When we return this vector as the final action in the function, Return Value Optimization(RVO) takes place to
// eliminate any extra copying of the vector
return files;
}
std::vector<std::string> listDirs(const std::vector<std::filesystem::path>& paths)
{
using Iterator = std::filesystem::directory_iterator;
std::vector<std::future<std::vector<std::string>>> futures; // listDir returns std::vector<std::string> type
// iterate over all the directory paths
for (const std::filesystem::path& path : paths)
{
// start each thread using std::async
futures.emplace_back(std::async(listDir, std::ref(path)));
}
std::vector<std::string> allFiles;
for (std::future<std::vector<std::string>>& fut : futures)
{
std::vector<std::string> files = fut.get(); // RVO
std::move(files.begin(), files.end(), std::back_inserter(allFiles));
}
// When we return this vector as the final action in the function, Return Value Optimization(RVO) takes place to
// eliminate any extra copying of the vector
return allFiles;
}
int main()
{
std::filesystem::path currentPath("G:\\lesson4");
std::vector<std::filesystem::path> paths;
for (Iterator it(currentPath); it!= Iterator(); ++it)
{
if (it->is_directory())
{
std::cout << it->path() << '\n';
paths.emplace_back(it->path());
}
}
for (const auto& fileName : listDirs(paths))
{
std::cout << fileName << std::endl;
}
}
在循环中,变量
path
是一个 引用。你可以把它想象成一个指针,但它不是。
for (const std::filesystem::path& path : paths)
{
// start each thread using std::async
futures.emplace_back(std::async(listDir, std::ref(path)));
}
在循环的第一次迭代中,
path
指的是向量paths
的第一个元素。在第二次迭代时,它指的是向量的第二个元素。等等...
因为
paths
在其元素的任何引用的生命周期内都不会改变(即使是在 futures
中使用的元素),所以这是安全的。当您使用 path
将 std::async
传递到 std::ref(path)
构造函数时,该引用包装器将封装 current 引用。
事实上,引用包装器通常使用底层的指针来实现,因为这是将引用作为左值传递的唯一实用方法。
即使在调用第一个异步方法之前循环移至第二次迭代,引用绑定也保持不变,并且 still 引用
paths
的第一个元素。