指向对象成员的迭代器

7

我承认我在想出一个合理的描述时遇到了困难。我想不到一个恰当的术语来精确地描述我所要寻找的内容。也许这可以称为“切片迭代器”。

假设我有这样一些东西:

struct S
{
    int i;
    char *s;
    float f;
};

std::vector<S> v(10);

我需要一种构造迭代器的方法,它可以指向成员。我希望能够将其传递给类似于的函数,而不需要在每种情况下都创建谓词。代码可能如下所示:
std::min_element(slicing_iterator(v.begin(), S::f), slicing_iterator(v.end(), S::f));

有没有模板技巧可以用来实现这个?或者它已经在Boost或其他库中完成了吗?

指向成员的指针不正是你想要的吗? - user2100815
调用此 min_element 的期望结果是什么?它将返回一个迭代器,该迭代器解引用为最小的 float,还是解引用为包含最小 S::fS 的迭代器? - Cubbi
这将是另一个指向具有最小 f 元素的 slicing_iterator。它不能是其他任何东西,因为迭代序列的唯一方法是通过 ++ 给定的迭代器。 - detunized
@detunized:Cubbi可能的意思是,如果“slicing_iterator”直接引用“S”实例,“min_element”将无法按照您的意图工作,因为它将尝试比较向量中所有这些“S”的值。另一方面,如果它引用“S::f”,则您将无法使用标准迭代器接口访问“S”。 - Boaz Yaniv
顺便问一下,你在用哪个编译器?如果你的编译器支持lambda表达式,那就是最好的选择。 - Boaz Yaniv
@Boaz Yaniv,它没有。 - detunized
4个回答

13

如果你正在寻找一种将S转换为其S::f的迭代器,那么可以使用boost来实现(什么不能使用boost呢?):

std::cout << *std::min_element(
               boost::make_transform_iterator(v.begin(), boost::bind(&S::f, _1)),
               boost::make_transform_iterator(v.end(), boost::bind(&S::f, _1))
              ) << '\n';

测试:https://ideone.com/jgcHr

但是,如果你正在寻找vector中S::f最小的S,那么谓词(predicate)是最合理的方法。


2

像这样的东西是否能胜任工作?

#include <algorithm>
#include <iostream>
#include <vector>

struct S
{
    int i;
    float f;

    S() : i(0), f(0.0f) {}
    S(int i_, float f_) : i(i_), f(f_) {}
};

template <typename Iterator, typename T, typename M>
class SlicingIterator : public std::iterator<typename Iterator::iterator_category,M>
{
private:
    Iterator m_it;
    M T::*m_m;
public:
    SlicingIterator(const Iterator& it, M T::*m)
    :   m_it(it), m_m(m)
    {}

    const M operator*() const
    {
        return (*m_it).*m_m;
    }

    bool operator!=(const SlicingIterator& rhs) const
    {
        return m_it != rhs.m_it;
    }

    SlicingIterator& operator++()
    {
        ++m_it;
        return *this;
    }

    bool operator<(const SlicingIterator& rhs) const
    {
        return m_it < rhs.m_it;
    }
};

template <typename Iterator, typename T, typename M>
SlicingIterator<Iterator,T,M> slicing_iterator(const Iterator& it, M T::*m)
{
    return SlicingIterator<Iterator,T,M>(it, m);
}

int main()
{
    std::vector<S> vec;
    vec.push_back(S(23,9));
    vec.push_back(S(17,10));
    std::copy(slicing_iterator(vec.begin(), &S::f), slicing_iterator(vec.end(), &S::f), std::ostream_iterator<float>(std::cout, " "));
    return 0;
}

2
除了已经建议的方法,您可以几乎完全按照代码示例所做的方式进行操作。
例如:
template< class IterT, class ObjT, class MemberT >
class slicing_iterator;

template< class IterT, class ObjT, class MemberT >
inline bool operator==(
                  const slicing_iterator<IterT,ObjT,MemberT>& a,
                  const slicing_iterator<IterT,ObjT,MemberT>& b
                  );

template< class IterT, class ObjT, class MemberT >
inline bool operator!=(
                  const slicing_iterator<IterT,ObjT,MemberT>& a,
                  const slicing_iterator<IterT,ObjT,MemberT>& b
                  );

template< class IterT, class ObjT, class MemberT >
class slicing_iterator
{
    IterT m_iter;
    MemberT ObjT::* m_member;

public:
    slicing_iterator( IterT iter, MemberT ObjT::*member ) :
        m_iter(iter), m_member(member)
    {
    }

    slicing_iterator& operator++() { ++m_iter; return *this; }
    slicing_iterator& operator--() { --m_iter; return *this; }

    MemberT& operator*() { return static_cast<ObjT&>(*m_iter).*m_member; }
    const MemberT& operator*() const { return static_cast<const ObjT&>(*m_iter).*m_member; }

    MemberT* operator->() { return &m_iter->*m_member; }
    const MemberT* operator->() const { return &m_iter->*m_member; }

private:
    friend bool operator== <IterT,ObjT,MemberT>(
                      const slicing_iterator<IterT,ObjT,MemberT>& a,
                      const slicing_iterator<IterT,ObjT,MemberT>& b
                      );
    friend bool operator!= <IterT,ObjT,MemberT>(
                      const slicing_iterator<IterT,ObjT,MemberT>& a,
                      const slicing_iterator<IterT,ObjT,MemberT>& b
                      );
};

template< class IterT, class ObjT, class MemberT >
inline bool operator==(
                  const slicing_iterator<IterT,ObjT,MemberT>& a,
                  const slicing_iterator<IterT,ObjT,MemberT>& b
                  )
{
    return a.m_iter == b.m_iter  &&  a.m_member == a.m_member;
}

template< class IterT, class ObjT, class MemberT >
inline bool operator!=(
                  const slicing_iterator<IterT,ObjT,MemberT>& a,
                  const slicing_iterator<IterT,ObjT,MemberT>& b
                  )
{
    return a.m_iter != b.m_iter  ||  a.m_member != a.m_member;
}

template< class IterT, class ObjT, class MemberT >
inline slicing_iterator<IterT,ObjT,MemberT>
make_slicing_iterator( IterT iter, MemberT ObjT::*member )
{
    return slicing_iterator<IterT,ObjT,MemberT>( iter, member );
}

struct S
{
    int i;
    char *s;
    float f;
};

int main(void)
{
    std::vector<S> v(10);

    std::min_element(
             make_slicing_iterator(v.begin(), &S::f),
             make_slicing_iterator(v.end(), &S::f)
             );
    return 0;
}

起初我没有注意到——这看起来与@Stuart Golodetz建议的类似,但优点是operator<不必为迭代器类型(例如std::list::iterator)定义。这使得此实现具有通用性。


2
如果您不想为每种情况创建一个谓词函数,我建议您不要寻找切片运算符,而是将谓词作为一个lambda函数实现(可以使用Boost或C++0x)。在这里您会找到详细的解释。

http://www.codeproject.com/KB/cpp/Sort.aspx

(这是关于 std::sort 的内容,但在 std::min_element 中的比较同样适用。)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接