【问题标题】:How to write a class capable of foreach如何编写一个能够 foreach 的类
【发布时间】:2009-10-26 17:25:53
【问题描述】:

自从 Visual Studio 添加对 foreach 扩展的支持以来已经有一段时间了

vector<int> v(3)
for each (int i in v) {
  printf("%d\n",i);
}

我想知道如何让任何类都能够使用 foreach。我需要实现一些接口吗?

【问题讨论】:

  • MS 编译器从不支持 C++ 代码中的任何内容。我猜你说的是其他语言。 C#?如果是这样,您需要重新标记您的问题。
  • 也许 c++ 标签应该是 vc++ 标签。这当然不是一个标准的 C++ 问题。
  • 像瘟疫一样避免这种情况。如果你必须懒惰并使用foreach。然后使用至少可移植的 boost 版本。

标签: c++ visual-studio iterator foreach


【解决方案1】:

VC++ 中的for each 语句,用于非托管类时:

for each (T x in xs)
{
    ...
}

只是语法糖:

for (auto iter = xs.begin(), end = xs.end(); iter != end; ++iter)
{
     T x = *iter;
}

其中auto 表示变量的类型是从初始化程序的类型自动推导出来的。

换句话说,您需要在您的类上提供begin()end() 方法,这些方法将为其返回开始和结束输入迭代器。

这是一个包装 istream 并允许您遍历其中的所有行的类示例:

#include <istream>
#include <iostream>
#include <fstream>
#include <string>


class lines
{
public:

    class line_iterator
    {
    public:

        line_iterator() : in(0)
        {
        }

        line_iterator(std::istream& in) : in(&in)
        {
            ++*this;
        }

        line_iterator& operator++ ()
        {
            getline(*in, line);
            return *this;
        }

        line_iterator operator++ (int)
        {
            line_iterator result = *this;
            ++*this;
            return result;
        }

        const std::string& operator* () const
        {
            return line;
        }

        const std::string& operator-> () const
        {
            return line;
        }

        friend bool operator== (const line_iterator& lhs, const line_iterator& rhs)
        {
            return (lhs.in == rhs.in) ||
                   (lhs.in == 0 && rhs.in->eof()) ||
                   (rhs.in == 0 && lhs.in->eof());
        }

        friend bool operator!= (const line_iterator& lhs, const line_iterator& rhs)
        {
            return !(lhs == rhs);
        }

    private:

        std::istream* const in;
        std::string line;
    };


    lines(std::istream& in) : in(in)
    {
    }

    line_iterator begin() const
    {
        return line_iterator(in);
    }

    line_iterator end() const
    {
        return line_iterator();
    }

private:

    std::istream& in;
};


int main()
{
    std::ifstream f(__FILE__);
    for each (std::string line in lines(f))
    {
        std::cout << line << std::endl;
    }
}

注意line_iterator 的实现实际上比for each 所需的最小值要大一些;但是,它是符合输入迭代器要求的最小实现,因此该类也可用于所有适用于输入迭代器的 STL 算法,例如std::for_eachstd::find 等。

【讨论】:

  • 你能举个最简单的例子来说明一个类实现那些可以编译的类吗?只需要知道方法的返回类型以及我缺少的任何其他内容,因为我无法使用简单的类进行编译
  • 您需要提供开始和结束迭代器。
  • 更新了答案以包含示例。实际返回类型无关紧要,只要它们支持for each 的描述扩展所隐含的所有操作。
  • @Pavel:这真的是输入迭代器吗?通常输入迭代器会为连续的*it 调用产生不同的值,并且它们的增量操作什么也不做。但是,这可能不是必需的,在这种情况下,这确实是一个输入迭代器。但在这种情况下,它仍然是过度设计的,因为输入迭代器不需要线成员。从*it 返回std::getline 的结果(作为右值)就足够了。
  • @sbi:你说得对,operator* 的递增实际上就足够了。然而,这本身并不是一个要求——这样做的能力源于++* 的定义以及它们与输入迭代器的交互,并且可以通过其他方式满足这些要求。无论如何,问题不在于迭代器本身,而在于for each,所以我选择了希望对于一个以前从未编写过任何类型的迭代器的人来说意图更清晰的代码,即使不是那么紧确实应该如此。
【解决方案2】:

你可以用这个

std::for_each

【讨论】:

  • +1 尽可能选择标准而不是非标准扩展。
  • std::for_each 并不是真正的替代品,因为它不允许您为每个内联迭代指定要执行的代码块。在实践中,我几乎从来没有看到 std::for_each 正是因为这个原因而使用 - 用迭代器编写一个好的旧 for 循环更容易。至于for each - 它在可移植代码中绝对没有任何位置,但并非所有代码都是有意义的可移植代码。如果您的应用程序已经使用 MFC 或 ATL 编写,那么无论如何您都会被 VC++ 困住,使用它的语言扩展也没有任何问题。
  • 好吧,如果不要求可移植性,我想将 lambda 函数放入池中并投票赞成这个答案。它们现在或很快就会被一些编译器实现,它们确实会使std::for_each 成为更好的解决方案,因为它们将成为(希望)所有编译器最终支持的标准。
【解决方案3】:

据我所知,foreach 不是 C++ 语言的一部分。虽然它在 C# 中。另外,我认为 STL 和/或 Boost 有一个 foreach 方法。也许你正在考虑那个?

【讨论】:

  • 我知道它不是语言的一部分,而且我也知道在 STL 和 Boost 中有一个 foreach。但是,Visual Studio 编译器以我作为编译器扩展展示的方式支持 foreach,我想知道如何制作可以使用它的类。
  • 啊,好的,误会了。
  • 因误解而被否决,然后为此道歉?原来有人投了反对票。
【解决方案4】:

你的类应该继承 IEnumerable 以使用 foreach

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-07-15
    • 1970-01-01
    • 2014-11-06
    • 2013-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多