这是一种使用惰性求值的有趣解决方案。首先,构造生成器对象enumerate_object
:
template<typename Iterable>
class enumerate_object
{
private:
Iterable _iter;
std::size_t _size;
decltype(std::begin(_iter)) _begin;
const decltype(std::end(_iter)) _end;
public:
enumerate_object(Iterable iter):
_iter(iter),
_size(0),
_begin(std::begin(iter)),
_end(std::end(iter))
{}
const enumerate_object& begin() const { return *this; }
const enumerate_object& end() const { return *this; }
bool operator!=(const enumerate_object&) const
{
return _begin != _end;
}
void operator++()
{
++_begin;
++_size;
}
auto operator*() const
-> std::pair<std::size_t, decltype(*_begin)>
{
return { _size, *_begin };
}
};
然后,创建一个包装函数 enumerate 来推断模板参数并返回生成器:
template<typename Iterable>
auto enumerate(Iterable&& iter)
-> enumerate_object<Iterable>
{
return { std::forward<Iterable>(iter) };
}
您现在可以这样使用您的功能:
int main()
{
std::vector<double> vec = { 1., 2., 3., 4., 5. };
for (auto&& a: enumerate(vec)) {
size_t index = std::get<0>(a);
double& value = std::get<1>(a);
value += index;
}
}
上面的实现只是一个玩具:它应该const
与非const
左值引用和右值引用一起工作,但考虑到它多次复制整个可迭代对象,对后者来说有实际成本。这个问题肯定可以通过额外的调整来解决。
从 C++17 开始,分解声明甚至允许您使用酷酷的类似 Python 的语法来直接在for
初始化程序中命名索引和值:
int main()
{
std::vector<double> vec = { 1., 2., 3., 4., 5. };
for (auto&& [index, value] : enumerate(vec)) {
value += index;
}
}
符合 C++ 的编译器分解auto&&
推断index
asstd::size_t&&
和value
as double&
。