【问题标题】:Is std::filesystem::directory_iterator really an iterator?std::filesystem::directory_iterator 真的是迭代器吗?
【发布时间】:2021-01-17 04:12:49
【问题描述】:

有些事情没有意义。根据我读到的内容,您可以像这样使用 std::filesystem :

#include <iostream>
#include <filesystem>
#include <string>
  
int main()
{
    auto iterator = std::filesystem::directory_iterator("c:/somefolder");
    for (auto& i : iterator)
    {
        i.exists();
        i.file_size();
    }
}

我将基于范围的循环理解为“对于迭代器中的每个 i,调用 i.file_size()”。使用 C++ 中的标准容器,这就是它的外观,例如标准矢量容器。

std::filesystem::directory_iterator 似乎不一致。迭代器应该指向容器中的元素,但是对于 std::filesystem::directory_iterator 它似乎是一个容器本身,对吧?基于范围的循环中的每个 i 都是一个“directory_entry”。

如果:

std::vector<int> container;
for (auto& i : container)

相当于:

std::vector<int> container;
for (auto it = std::vector<int>::iterator; it != container.end(); it++)

是什么:

for (auto i : iterator)

相当于?

上面基于范围的循环发生了什么?将该循环读作“迭代器中的每个 i”是错误的吗? i 值是 std::filesystem::directory_entry,但在循环中迭代的是什么?什么容器?

【问题讨论】:

  • A directory_iterator is 是一个有效的迭代器,您可以按常规方式使用它(递增、比较、取消引用)。它也可以以你的显示方式使用,作为一个范围。
  • @BobTFish 在这个例子中,什么是迭代器?如果没有基于范围的 for 循环,这将如何编写,所以我可以理解。
  • 似乎停留在对容器的需求上,而不是迭代器设计模式的意图。就像提供一个通用的迭代接口。您的频道向上/向下按钮是迭代器。您的第一个代码块也是无意义的。 exists() 返回一个布尔值,但您不关心它或对其进行任何检查。您也不会对文件大小做任何事情。正如答案所指出的那样,您似乎也误解了基于范围的 for 循环所需要的内容。

标签: c++ iterator filesystems range-based-loop


【解决方案1】:

根据this referenceLegacyInputIterator。所以是的,它是一个“真正的”迭代器。

有重载的begin and end 函数。 begin 函数返回未修改的迭代器,end 函数不使用参数,而是返回默认构造的迭代器。它们的存在只是为了支持 range-for 循环。

如果你有:

auto iterator = std::filesystem::directory_iterator("c:/somefolder");

然后

iterator == begin(iterator) && std::filesystem::directory_iterator() == end(iterator)

会是真的。


请注意,由于begin 将返回未修改的迭代器,即使在您执行iterator++ 之后,条件iterator == begin(iterator) 也会为真。


要“手动”迭代目录,您只需几乎像任何其他迭代器一样:

for (auto iterator = std::filesystem::directory_iterator("c:/somefolder");
     iterator != std::filesystem::directory_iterator();
     iterator++)
{
    // Use the iterator
    std::cout << "The path is " << iterator->path() << '\n';
}

【讨论】:

  • 迭代器正在迭代 std::filesystem::directory_entry,那么我将如何手动迭代呢? directory_entry 没有 begin() 和 end()。
  • @Zebrafish directory_iterator 是一个合适的迭代器,就像任何其他迭代器一样。解引用它会返回一个directory_entry 结构,就像任何其他迭代器在解引用时返回一些其他类型一样(例如std::vector&lt;int&gt;::iterator 返回一个int)。
  • 是的,只是迭代器通常不是“:”之后基于范围的循环的第二部分,是吗?
  • @Zebrafish 那是因为 std::beginstd::end 函数重载。 range-for 中: 的右侧不需要有beginend 成员函数,只需在正确的范围内重载beginend 函数。正如我在回答中提到的那样,directory_iterator 的重载是在考虑范围迭代的情况下进行的,并且以允许迭代器这样使用的方式实现。
【解决方案2】:

基于范围的for循环使用overloads of std::begin and std::end

directory_iterator begin( directory_iterator iter ) noexcept;
directory_iterator end( const directory_iterator& ) noexcept;

注意在directory_iterator 上调用begin 如何返回directory_iterator 本身,而end 忽略它的参数并“返回一个默认构造的directory_iterator,它用作结束迭代器。”

【讨论】:

    【解决方案3】:

    基于范围的 for 循环并不总是需要容器才能工作。它还可以在一个范围内循环,这就是它在这种情况下所做的事情。

    基于范围的 for 循环结构如下:

    auto && __range = range_expression ;
    for ( ; begin_expr != end_expr ; ++begin_expr) {
    
        range_declaration = *__begin;
        loop_statement
    
    } 
    

    begin_exprend_expr are 的位置:

    否则,begin_exprbegin(__range)end_exprend(__range),它们是通过参数相关查找找到的(非 ADL 不执行查找)。

    由于directory_iterator 重载了beginend,它的功能就像普通的LegacyInputIterator

    for (auto i : iterator)
    

    我们实际上并没有像您对容器那样“循环遍历迭代器”,因为迭代器本身不是您提到的容器,我们正在访问迭代器并重复递增它直到到达end() .这里的容器是directory_entry

    【讨论】:

    • 如果它正在遍历 directory_entry,为什么 directory_entry 没有 begin() 和 end() 可以比较?如果没有基于范围的循环,这将如何编写,以便我理解?
    • @Zebrafish directory_entry 没有公开另一种方式(在撰写本文时)来迭代其文件。 operator* 在这个迭代器上做一些特定于操作系统的事情。
    猜你喜欢
    • 2019-02-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-27
    相关资源
    最近更新 更多