Итератор, "указывающий" на член объекта - PullRequest
7 голосов
/ 29 апреля 2011

Я признаю, что мне было трудно придумать разумное описание для этого.Я не могу придумать хорошего термина, который бы точно описывал то, что я ищу.Возможно, это можно назвать итератором среза .

Допустим, у меня есть что-то вроде этого:

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

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

Я ищу способ создания итератора, который бы указывал на член S.Я хотел бы иметь возможность передать его как-то типа std::min_element без создания предиката в каждом случае.Что-то, что может выглядеть так:

std::min_element(slicing_iterator(v.begin(), S::f), slicing_iterator(v.end(), S::f));

Есть ли какой-нибудь шаблонный прием, который я мог бы использовать для достижения этой цели?Или, может быть, это уже сделано где-то в Boost или какой-то другой библиотеке?

Ответы [ 4 ]

13 голосов
/ 29 апреля 2011

Если вы ищете итератор, который преобразует S в его S :: f, это, безусловно, можно сделать с помощью 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

Но если вы ищете S, чье S :: f является наименьшим в векторе, предикат является наиболее разумным подходом.

2 голосов
/ 29 апреля 2011

В дополнение к тому, что уже было предложено, вы можете сделать это почти так же, как пример кода.

Пример:

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, но преимущество в том, что оператор <не обязательно должен быть определен для типа итератора (например, std :: list :: iterator).Это делает эту реализацию универсальной. </p>

2 голосов
/ 29 апреля 2011

Будет ли что-то подобное делать работу?

#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 голосов
/ 29 апреля 2011

Если вы не хотите создавать предикатную функцию для каждого случая, я бы предложил не искать оператор среза, а реализовать ваш предикат как лямбда-функцию (используя Boost или C ++ 0x).Здесь вы найдете подробное объяснение

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

(это примерно std::sort, но сравнение в std::min_element работает одинаково.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...