neo*_*eop 3 c++ stdvector c++20 std-filesystem std-ranges
我正在尝试使用其insert(const_iterator pos, InputIt first, InputIt last)成员函数模板将转换后的目录条目范围插入向量中。不幸的是,我无法在 GCC 下编译以下代码,11.1.0根据https://en.cppreference.com/w/cpp/compiler_support应该有范围支持。
#include <filesystem>
#include <vector>
#include <ranges>
#include <iterator>
namespace fs = std::filesystem;
namespace ranges = std::ranges;
namespace views = std::views;
// no solution
namespace std {
template <typename F>
struct iterator_traits<ranges::transform_view<ranges::ref_view<fs::directory_iterator>, F>> {
using iterator_category = std::input_iterator_tag;
};
}
int main() {
std::vector<fs::path> directory_tree;
auto subdir = fs::directory_iterator(".");
ranges::input_range auto subdir_names = subdir
| views::transform([](const auto& entry) { return entry.path(); /* can be more complex*/ })
| views::common;
// replacing subdir_names with subdir works
std::input_iterator auto b = ranges::begin(subdir_names);
std::input_iterator auto e = ranges::end(subdir_names);
directory_tree.insert(
directory_tree.begin(),
b,
e
);
}
Run Code Online (Sandbox Code Playgroud)
错误信息主要说:
error: no matching function for call to 'std::vector<std::filesystem::__cxx11::path>::insert(std::vector<std::filesystem::__cxx11::path>::iterator, std::ranges::transform_view<std::ranges::ref_view<std::filesystem::__cxx11::directory_iterator>, main()::<lambda(const auto:16&)> >::_Iterator<false>&, std::ranges::transform_view<std::ranges::ref_view<std::filesystem::__cxx11::directory_iterator>, main()::<lambda(const auto:16&)> >::_Iterator<false>&)'
Run Code Online (Sandbox Code Playgroud)
再往下:
error: no type named ‘iterator_category’ in ‘struct std::iterator_traits<std::ranges::transform_view<std::ranges::ref_view<std::filesystem::__cxx11::directory_iterator>, main()::<lambda(const auto:15&)> >::_Iterator<false> >’
Run Code Online (Sandbox Code Playgroud)
我试图将上述专业化添加到std::iterator_traits相关的迭代器类型,但无济于事。我想了解为什么这不能编译,如果可能的话,如何修复它。我想避免创建临时向量。
让我知道是否需要更多 gcc 的错误消息。
fs::directory_iterator是输入范围。这意味着当您通过它进行调整时,transform您仍然可以获得输入范围(自然)。这个转换范围的迭代器有一个后缀operator++,返回void.
这可以说是 C++98 迭代器模型中的一个缺陷,它甚至仍然需要输入迭代器有一个operator++返回原始类型的后缀。即使这必然是一个悬空操作。在 C++20 迭代器模型中,后缀增量可以void为输入迭代器返回。
因此,您返回的转换范围(views::common无操作,它已经是common)是C++20 输入范围(正如您正在验证的那样),但它不是任何类型的 C++98/C++17 范围因为它的迭代器由于后缀增量规则甚至不满足Cpp17InputIterator - 所以它的迭代器甚至不费心提供iterator_category.
这使得:
directory_tree.insert(directory_tree.begin(), b, e);
Run Code Online (Sandbox Code Playgroud)
失败,因为该功能预计类型满足Cpp17InputIterator,并b与e别。
解决方法是改为执行以下操作:
ranges::copy(subdir_names, std::inserter(directory_tree, directory_tree.begin()));
Run Code Online (Sandbox Code Playgroud)
或者甚至将两个步骤结合起来:
ranges::copy(subdir
| views::transform([](const auto& entry) { return entry.path(); /* can be more complex*/ }),
std::inserter(directory_tree, directory_tree.begin())
);
Run Code Online (Sandbox Code Playgroud)
在这里,我们只要求源范围是 C++20 input_range(它是)。
目的是很快您将能够编写:
directory_tree.insert_range(
directory_tree.begin(),
subdir
| views::transform([](const auto& entry) { return entry.path(); }));
Run Code Online (Sandbox Code Playgroud)
但这不会直到 C++23。