如何在C++中实现输入迭代器的"dereference and post-increment"?
How to implement "dereference and post-increment" for input iterator in C++?
InputIterator 的要求*i++
包括等效表达式为
value_type x = *i;
++i;
return x;
如何在不实现标准的后增量i++
返回非 void 值的情况下声明这样的运算符(输入迭代器不需要这样做(?
您可以使用代理来增加帖子:
#include <iostream>
class input_iterator
{
private:
class post_increment_proxy
{
public:
post_increment_proxy(int value) : value(value) {}
int operator * () const { return value; }
private:
int value;
};
public:
post_increment_proxy operator ++ (int) {
post_increment_proxy result{value};
++value;
return result;
}
private:
int value = 0;
};
int main() {
input_iterator i;
std::cout << *i++ << 'n';
std::cout << *i++ << 'n';
std::cout << *i++ << 'n';
}
首先,迭代器是可复制的,即使在 InputIterator 的情况下,副本的行为更像是一个移动(具体来说,在你增加迭代器的任何一个副本之后,你不应该取消引用它的任何其他副本(。
尽管如此,复制迭代器应该没有任何问题 - 事实上,大多数库(以及许多其他代码(都假设迭代器是"轻量级"对象;复制它们很便宜,所以(对于一个明显的例子(它们通常是按值传递的,而不是通过引用传递的。
因此,稍微简化的流迭代器可能如下所示:
template <class T>
class istream_iterator {
std::istream *is;
T data;
public:
istream_iterator(std::istream &is) : is(&is) { ++(*this); }
istream_iterator() : is(nullptr) { }
istream_iterator &operator++() { (*is) >> data; return *this; }
// So here's the post-increment: it just saves a copy of itself, then
// reads the next item (which increments the iterator), and finally
// returns the copied object, which will return the previously-read item
// from the stream when/if dereferenced.
istream_iterator operator++(int) {
// Note: this uses the compiler-generated copy constructor. Assuming
// a `T` is copy-constructible, this works fine--other than a T,
// we're only copying a pointer.
istream_iterator temp = *this;
(*is) >> data;
return temp;
}
T const &operator*() const { return data; }
bool operator !=(istream_iterator &end) { return (*is).good(); }
bool operator ==(istream_iterator &end) { return !(*is).good(); }
};
这在几个相当小的点上"作弊"——例如,两个默认构造的迭代器应该相互比较,这不会费心实现(而且几乎没有人使用或关心(。在正常使用中,从流创建一个迭代器,并默认构造另一个迭代器。当且仅当第一个到达输入流的末尾(或由于某种原因读取失败(时,两者之间的比较应返回 true。同样,这省略了实现operator->
,以及标准迭代器类型(value_type、traits_type、istream_type等(所需的一些typedef
。不过,这些都与手头的问题无关(所有这些都是添加所需代码的问题,而不是对已经存在的代码进行任何实质性更改(。
代码的快速演示可能如下所示:
int main() {
istream_iterator<char> i(std::cin), end;
while (i != end)
std::cout << *i++;
}
这会将字符从标准输入复制到标准输出,跳过空格,因为默认情况下operator>>
跳过空格(但如果需要,您可以使用noskipws
消除空格(。
我也曾经遇到过这种情况。IMO,这个要求毫无意义。这是输入迭代器实现者的负担,并且该行为可以在迭代器的用户方面非常轻松地实现。
我的看法是这样的:
// -- inputitproxy.hpp ------
template <typename It, bool backward>
class increment_decrement_proxy
{
It& it;
bool incremented = false;
void push()
{
if (!incremented)
{
if constexpr (backward) --it; else ++it;
}
}
public:
increment_decrement_proxy() = delete;
increment_decrement_proxy(const increment_decrement_proxy&) = delete;
increment_decrement_proxy(increment_decrement_proxy&&) = delete;
increment_decrement_proxy(It& it) : it{it} {}
auto operator*() -> decltype(*it)
{
decltype(auto) result = *it;
push();
incremented = true;
return result;
}
~increment_decrement_proxy() { push(); }
// ~increment_decrement_proxy() && { push(); }
// ~increment_decrement_proxy() & = delete;
};
template <typename It>
using increment_proxy = increment_decrement_proxy<It, 0>;
template <typename It>
using decrement_proxy = increment_decrement_proxy<It, 1>;
你像这样使用它:
// -- inputit.hpp -----------
template <typename T>
class input_it
{
T* ptr;
public:
// boilerplate
input_it(T* ptr) : ptr{ptr} {}
T& operator*() const { return *ptr; }
T* operator->() const { return ptr; }
input_it& operator++() { ++ptr; return *this; }
input_it& operator--() { --ptr; return *this; }
// actual usage:
auto operator++(int) { return increment_proxy{*this}; }
auto operator--(int) { return decrement_proxy{*this}; }
};
一个工作示例:
#include "inputit.hpp"
#include <iostream>
struct no_copy
{
int x;
explicit no_copy(int i) : x(i) {}
no_copy(const no_copy&) = delete;
};
int main()
{
no_copy stuff[] { no_copy{1}, no_copy{2}, no_copy{3} };
input_it it { &stuff[0] };
std::cout << it->x << std::endl; // prints 1
(void)it++;
// Note: an input iterator is not supposed to be used like that.
//auto it2 = it++; // compiles, but does not actually increment
std::cout << it->x << std::endl; // prints 2
std::cout << (*it++).x << std::endl; // prints 2 again
std::cout << it->x << std::endl; // prints 3
}
两个注释掉的析构函数不起作用,因为析构函数上不允许引用注释。如果这样做有效,则初始化it2
的行将无法编译。
你可以在这里尝试一下。
- C++核心准则 C35 对于接口类"A base class destructor should be either public and virtual, or protected and nonv
- 为什么C++逐位AND运算符在不同大小的操作数中表现为这样
- 为什么 Clang 不允许"and"作为函数名称?
- 位阵列上的快速AND运算
- 是否可以在 C++03 中定义'move-and-swap idiom'等效项
- BoostPython and CMake
- OpenSSL BIO and SSL_read
- Gurobi GRBModel and GRBmodel in C++
- std::visit and std::variant usage
- SHBrowseForFolder with BIF_BROWSEFORCOMPUTER and SHGetPathFr
- Directx12 and keystrokes
- different between int **arr =new int [ n]; and int a[i][j]?
- C++ getenv and setenv
- Inference pytorch C++ with alexnet and cv::imread image
- Visual Studio 2019 C++ and std::filesystem
- 保证逻辑 AND 表达式中的函数调用
- python ctypes and C++ pointers
- C++ const char with .begin() and .end()
- Threads with Classes and std::packaged_task
- 如何在C++中实现输入迭代器的"dereference and post-increment"?