12

我有这样的课:

class Foo {
private:
    int a,b,c,d;
    char bar;
    double m,n
public:
    //constructors here
};

我想在我的课堂上允许 range-for 循环,例如

Foo foo {/*...*/};
for(auto& f : foo) {
  //f will be a specific order such as c,b,d,(int)m,(int)bar,a,(int)n
}

我怎样才能做到这一点?我正在查看迭代器,但不知道 range-for 循环的要求是什么。(请不要让我使用数组或 STL 类型)

4

3 回答 3

13

循环定义为等价于:

for ( auto __begin = <begin-expr>,
           __end = <end-expr>;
      __begin != __end;
      ++__begin ) {
    auto& f = *__begin;
    // loop body
}

where <begin-expr>is foo.begin(),或者begin(foo)如果没有合适的成员函数,同样 for <end-expr>。(这是 C++11 6.5.4 中规范的简化,适用于范围是类类型的左值的这种特殊情况)。

所以需要定义一个支持预增++it、解引用*it和比较的迭代器类型i1 != i2;并且要么

  • 赋予foo公共成员功能begin()end();或者
  • 在同一命名空间中定义非成员函数begin(foo)和,以便它们可以通过依赖于参数的查找找到。end(foo)foo
于 2013-08-19T19:10:26.900 回答
1

这看起来很不像 C++,而且很容易损坏。如果在未来的某些更新期间迭代顺序发生变化(意外或无意)怎么办?依赖特定订单的客户将中断。

如果您希望支持这一点,您所要做的就是实现自己的迭代器并提供begin/end方法(或具有这些名称的自由函数)来提供访问权限。然后迭代器负责记住它当前正在查看的属性并在取消引用时提供它。

于 2013-08-19T19:11:14.513 回答
0

这是我提出的一个基本框架:

#include <iterator>

struct Foo;

template<typename Type>
struct MemberPtrBase {
    virtual ~MemberPtrBase() { }

    virtual Type get() const = 0;
    virtual MemberPtrBase & set(Type const &) = 0;
};

template<typename Class, typename RealType, typename CommonType>
struct MemberPtr : MemberPtrBase<CommonType> {
public:
    MemberPtr(Class * object, RealType(Class::*member))
    : m_object(object), m_ptr(member)
    { }

    CommonType get() const {
        return m_object->*m_ptr;
    }

    MemberPtr & set(CommonType const & val) {
        m_object->*m_ptr = val;
        return *this;
    }

    MemberPtr & operator=(RealType const & val) {
        return set(val);
    }

    operator CommonType() const {
        return get();
    }
private:
    Class * m_object;
    RealType (Class::*m_ptr);
};

template<typename Class, typename... Types>
struct MemberIterator {
public:
    using CommonType = typename std::common_type<Types...>::type;
public:
    MemberIterator(Class & obj, std::size_t idx, Types(Class::*...member))
    : m_object(obj), m_index(idx), m_members { new MemberPtr<Class, Types, CommonType>(&obj, member)... }
    { }

    MemberPtrBase<CommonType> & operator*() const {
        return *m_members[m_index];
    }

    bool operator==(MemberIterator const & it) const {
        return (&m_object == &it.m_object) && (m_index == it.m_index);
    }

    bool operator!=(MemberIterator const & it) const {
        return (&m_object != &it.m_object) || (m_index != it.m_index);
    }

    MemberIterator & operator++() {
        ++m_index;
        return *this;
    }
private:
    Class & m_object;
    std::size_t m_index;
    MemberPtrBase<CommonType> * m_members[sizeof...(Types)];
};

struct Foo {
public:
    using iterator = MemberIterator<Foo, int, int, int, int>;
public:
    Foo(int a, int b, int c, int d)
    : m_a(a), m_b(b), m_c(c), m_d(d)
    { }

    iterator begin() {
        return iterator(*this, 0, &Foo::m_b, &Foo::m_d, &Foo::m_c, &Foo::m_a);
    }

    iterator end() {
        return iterator(*this, 4, &Foo::m_b, &Foo::m_d, &Foo::m_c, &Foo::m_a);
    }
private:
    int m_a, m_b, m_c, m_d;
};

如果您对可变参数模板有基本的了解,我认为代码是不言自明的。

用法很简单:

#include <iostream>
int main(int argc, char ** argv) {
    Foo foo { 1, 2, 3, 4 };

    for(auto & mem : foo) {
        std::cout << mem.get() << std::endl;
        mem.set(3);
    }

    for(auto & mem : foo) {
        std::cout << mem.get() << std::endl;
    }
}

可以在ideone上找到 POC

于 2013-08-19T19:49:21.480 回答