如何使用基于范围的循环创建自定义类来循环STL容器中的连续项目对?
这是我想要的语法和输出:
std::list<int> number_list;
number_list.push_back(1);
number_list.push_back(2);
number_list.push_back(3);
auto paired_list = Paired(number_list);
for (const auto & pair : paired_list) {
std::printf("The pair is (%d, %d)\n", *(pair[0]), *(pair[1]));
// or
//std::printf("The pair is (%d, %d)\n", *(pair.first), *(pair.second));
}
// output:
// The pair is (1, 2)
// The pair is (2, 3)
我知道这些(以及更多)是必需的,但我无法弄清楚:
template <class T>
class Paired {
???
class iterator {
???
}
iterator begin() {
...
}
iterator end() {
...
}
}
不要担心const
修饰符。
没有提升。
请勿修改或复制容器中的对象。
答案 0 :(得分:18)
这就是我要做的事。
#include <iterator>
#include <utility>
template <typename FwdIt> class adjacent_iterator {
public:
adjacent_iterator(FwdIt first, FwdIt last)
: m_first(first), m_next(first == last ? first : std::next(first)) { }
bool operator!=(const adjacent_iterator& other) const {
return m_next != other.m_next; // NOT m_first!
}
adjacent_iterator& operator++() {
++m_first;
++m_next;
return *this;
}
typedef typename std::iterator_traits<FwdIt>::reference Ref;
typedef std::pair<Ref, Ref> Pair;
Pair operator*() const {
return Pair(*m_first, *m_next); // NOT std::make_pair()!
}
private:
FwdIt m_first;
FwdIt m_next;
};
template <typename FwdIt> class adjacent_range {
public:
adjacent_range(FwdIt first, FwdIt last)
: m_first(first), m_last(last) { }
adjacent_iterator<FwdIt> begin() const {
return adjacent_iterator<FwdIt>(m_first, m_last);
}
adjacent_iterator<FwdIt> end() const {
return adjacent_iterator<FwdIt>(m_last, m_last);
}
private:
FwdIt m_first;
FwdIt m_last;
};
template <typename C> auto make_adjacent_range(C& c) -> adjacent_range<decltype(c.begin())> {
return adjacent_range<decltype(c.begin())>(c.begin(), c.end());
}
#include <iostream>
#include <vector>
using namespace std;
void test(const vector<int>& v) {
cout << "[ ";
for (const auto& p : make_adjacent_range(v)) {
cout << p.first << "/" << p.second << " ";
}
cout << "]" << endl;
}
int main() {
test({});
test({11});
test({22, 33});
test({44, 55, 66});
test({10, 20, 30, 40});
}
打印:
[ ]
[ ]
[ 22/33 ]
[ 44/55 55/66 ]
[ 10/20 20/30 30/40 ]
注意:
我没有对此进行详尽的测试,但它尊重前向迭代器(因为它不会尝试使用超出++,!=和*的操作。)
range-for的要求非常低;它不需要前向迭代器应该提供的所有东西。因此,我已经达到了range-for的要求,但没有了。
“NOT m_first”注释与如何接近范围的结尾有关。从空范围构造的adjacent_iterator具有m_first == m_next,其也是== last。从1元素范围构造的adjacent_iterator的m_first指向元素,m_next == last。从多元素范围构造的adjacent_iterator具有指向连续有效元素的m_first和m_next。随着它的增加,最终m_first将指向最后一个元素,m_next将是最后一个。 adjacent_range的end()返回的内容是从(m_last,m_last)构造的。对于完全空的范围,这与begin()在物理上相同。对于1+元素范围,这与已经递增的begin()在物理上不相同,直到我们没有完整的对 - 这样的迭代器使m_first指向最终元素。但是如果我们根据它们的m_next来比较迭代器,那么我们就会得到正确的语义。
“NOT std :: make_pair()”注释是因为make_pair()衰减,而我们实际上需要一对引用。 (我本可以使用decltype,但iterator_traits也会告诉我们答案。)
主要的剩余细微之处将围绕禁止右撇子作为make_adjacent_range的输入(这样的临时演员不会延长他们的生命;委员会正在研究这个问题),并且扮演ADL舞蹈以尊重非成员的开始/结束,以及内置数组。这些练习留待读者阅读。
答案 1 :(得分:3)
编辑我使用的是transform
。
第二个版本采用二进制函数,将两个值转换为新值 (不同)价值:
string make_message(int first, int second) {
ostringstream oss;
oss << "The pair is (" << first << ", " << second << ")";
return oss.str();
}
我们现在可以将相邻对转换为第三个范围。我们将使用ostream_iterator
来使用cout
,例如范围:
list<int> numbers;
//...
adjacent_difference(numbers.begin(), numbers.end(),
ostream_iterator<string>(cout, "\n"),
make_message);
第二次编辑
我在comp.lang.c++.moderated上发现了一个问题,询问为什么标准库中没有更多“邻近”函数,例如for_each_adjacent
。回复称使用std::mismatch
实现它们是微不足道的。
我认为这比实现一个特殊的相邻迭代器更好。
答案 2 :(得分:0)
试试这个。
#include <list>
#include <iostream>
template<class T, class TIter = typename T::iterator, class TVal = typename T::value_type>
class PairedImpl {
T& m_t;
public:
class Iter {
TIter m_it;
public:
Iter(const TIter & it) : m_it(it) {}
bool operator!=(const Iter& it) { return m_it != it.m_it; }
Iter& operator++() { ++m_it; return *this; }
const Iter & operator *() const { return *this; }
const TVal & first() const { return *m_it; }
const TVal & second() const { return *std::next(m_it); }
};
PairedImpl(T& t) : m_t(t) {}
Iter begin() { return Iter(m_t.begin()); }
Iter end() {
TIter end = m_t.end();
return Iter(m_t.empty() ? end : --end);
}
};
template<class T>
PairedImpl<T> Paired(T& t) {
return PairedImpl<T>(t);
}
用法
int main()
{
std::list<int> lst;
lst.push_back(1);
lst.push_back(2);
lst.push_back(3);
lst.push_back(4);
lst.push_back(5);
for (const auto & pair : Paired(lst)) {
std::cout << "(" << pair.first() << ", " << pair.second() << ")" << std::endl;
}
return 0;
}
答案 3 :(得分:-1)
好的,一小时没有答案,我想出了一个有效的解决方案。请注意,这使用我自己的FixedLengthVector
,这听起来就像它。
template <class T>
class Grouped {
private:
// length of grouped objects
static const unsigned length_ = 2;
// hold pointer to base container to avoid comparing incompatible iterators
T * base_container_;
public:
// constructor
Grouped(T & base_container) :
base_container_(&base_container) {
}
// iterator
class iterator {
private:
// hold pointer to base container to avoid comparing incompatible iterators
T * base_container_;
// hold pointers to objects in base container
FixedLengthVector<length_, typename T::value_type *> ptr_;
// hold iterator to last object
typename T::iterator last_iterator_;
public:
// constructor
iterator(T & base_container, typename T::iterator & it)
: base_container_(&base_container),
last_iterator_(it) {
// set up pointers if possible
unsigned i = 0;
// check for end iterator
if (last_iterator_ == base_container_->end()) {
ptr_.fill(NULL);
return;
}
// set up first object
ptr_[0] = &*last_iterator_;
// set up next objects
for (unsigned i = 1; i < length_; ++i) {
++last_iterator_;
if (last_iterator_ == base_container_->end()) {
ptr_.fill(NULL);
return;
}
ptr_[i] = &*last_iterator_;
}
}
// dereference operator
FixedLengthVector<length_, typename T::value_type *> & operator * (void) {
assert(ptr_[0] != NULL);
return ptr_;
}
// pre-increment
iterator & operator ++ (void) {
// can't increase past end
assert(last_iterator_ != base_container_->end());
// find next iterator
++last_iterator_;
if (last_iterator_ == base_container_->end()) {
ptr_.fill(NULL);
return * this;
}
// cycle pointers left
for (unsigned i = 1; i < length_; ++i) {
ptr_[i - 1] = ptr_[i];
}
ptr_[length_ - 1] = &*last_iterator_;
return * this;
}
// equality comparison
bool operator == (const iterator & that) const {
return base_container_ == that.base_container_ &&
last_iterator_ == that.last_iterator_;
}
// inequality comparison
bool operator != (const iterator & that) const {
return !(*this == that);
}
};
// end iterator
iterator end() {
return iterator(*base_container_, base_container_->end());
}
// begin iterator
iterator begin() {
return iterator(*base_container_, base_container_->begin());
}
};