下面是使用某種有趣的解決方案懶惰的評價。首先,構建發電機對象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 };
}
};
然後,創建一個包裝函數枚舉會推斷出模板參數和返回發電機:
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
左值參考工作s以及右值引用,但是對於後者來說,它具有實際的代價,考慮到它複製整個可迭代對象多次。這個問題肯定可以通過額外的調整來解決。
由於C++ 17,分解聲明甚至允許你有涼爽的Python的語法來命名索引並直接在for
初始值:
int main()
{
std::vector<double> vec = { 1., 2., 3., 4., 5. };
for (auto&& [index, value] a: enumerate(vec)) {
value += index;
}
}
我沒有一個C++ 17兼容的編譯器來檢查它,但我希望分解中的auto&&
能夠推斷index
爲std::size_t
和value
爲double&
。
爲什麼簡化簡單的事情? :-) – Kos
您將不得不創建一個類似生成器的函數/對象,它返回std :: pair並使用該對的第一個和第二個字段。你可以使用宏來實現這個技巧,但是在C++中使用類似Python的語法沒有方便和優雅的方法。你的第二個解決方案可能是最好的。 – Morwenn
@Kos我對解決方案2非常好。只是好奇,如果有更簡單的方法:) – betabandido