0

通常我不能使用 std::for_each 因为我对特定元素的逻辑取决于它的当前索引。为此,我发明了一个函子类,它包装了主函子并将当前索引传递给它。理想情况下,我想将它与 lambda 表达式一起使用。我创建的课程安全有效吗?有没有更好的解决方案?我确实希望包装器的运算符 () 返回 lambda 表达式的类型,但我无法弄清楚。另外,我应该为索引使用什么类型?我应该通过值还是引用将主函子存储在包装器中?

谢谢!

template<class FUNC>
class IndexFunctor
{
public:
    typedef FUNC FUNC_T;

    explicit IndexFunctor(const FUNC_T& func) : func(func), index(0) {}

    // how can this return the return type of func?
    template<class T>
    void operator ()(T& param)
    {
        func(index++, param);
    }

    const FUNC_T& GetFunctor() const
    {
        return func;
    }

    int GetIndex() const
    {
        return index;
    }

    void SetIndex(int index)
    {
        this->index = index;
    }

private:
    FUNC_T func;
    int index;
};

template<class FUNC>
IndexFunctor<FUNC> with_index(const FUNC& func)
{
    return IndexFunctor<FUNC>(func);
}

void somefunc()
{
    std::vector<int> v(10);
    std::for_each(v.begin(), v.end(), with_index([](int index, int x){ std::cout << "[" << index << "]=" << x << std::endl; }));
}
4

4 回答 4

4

您从一开始的问题是您想使用 ? 跟踪索引/计数器std::for_each?然后一种解决方案是简单地使用一个计数器变量并让例如 lambda 表达式捕获它。

std::vector<int> v{ 1, 2, 3 };

std::vector<int>::size_type i = 0;

std::for_each(begin(v), end(v), [i] (int x) mutable {
    std::cout << "[" << i++ << "]=" << x << std::endl;
});

或者你可以简单地这样做:

std::vector<int>::size_type i = 0;
for (auto itr = begin(v); itr != end(v); ++itr) {
    std::cout << "[" << i++ << "]=" << *itr << std::endl;
}

在 C++11 中并使用基于范围的 for 循环可以简化为:

std::vector<int>::size_type i = 0;
for (auto itr : v) {
    std::cout << "[" << i++ << "]=" << itr << std::endl;
}

注意:在上面最后两个示例中,您必须记住每次运行 for 循环时都将索引计数器重置为 0。

更新:如果你想遍历一个跟踪索引的子范围,同时又不想在循环之外有一个索引变量,你可以使用std::distance如下方式计算索引:

for (auto itr = begin(v); itr != end(v); ++itr) {
    std::cout << "[" << std::distance(begin(v),itr) << "]=" << *itr << std::endl;
}

同样使用 C++14 通用 Lambda 捕获表达式,您可以省略外部范围内的计数器变量,并简单地使用初始化表达式创建i为 lambda 的成员:

std::for_each(begin(v), end(v), [i = 0] (int x) mutable {
    std::cout << "[" << i++ << "]=" << x << std::endl;
});
于 2013-07-21T21:55:32.777 回答
4

这应该是安全的,尽管自己编写索引 for-each 相当简单。

template <typename TInputIterator, typename TFunc>
TFunc counted_for_each(TInputIterator first, TInputIterator last, TFunc func)
{
    for (size_t i = 0; first != last; ++first)
    {
        func(i++, *first);
    }

    return func;
}

更少的代码并完成同样的事情。

于 2013-07-21T21:35:04.943 回答
3

在我看来std::for_each,基于范围的循环基本上已经过时,for( : )除了迭代子范围之外。

即便如此,我也只是有一个range_view template适用于基于范围的for( : )循环。Arange_view是一个简单的结构,带有返回迭代器begin()end()方法:这样的对象可以传递给基于范围的 for 循环:

template<typename Iterator>
struct range_view {
  Iterator b, e;
  Iterator begin() const { return b; }
  Iterator end() const { return e; }
};
template<typename Iterator>
range_view<Iterator> make_range_view( Iterator b, Iterator e ) {
  return {b,e};
}
template<typename Container>
auto make_range_view( Container&& c )
-> decltype( make_range_view( std::begin(c), std::end(c) ) )
{   return ( make_range_view( std::begin(c), std::end(c) ) ); }

如果我需要索引,我会使用一个indexes范围,或者如果非常热衷zip于在索引范围和原始容器上做一个范围适配器(生成一个元组范围)。 indexes是一个迭代器连续整数(boost具有此类类型),并且zip是采用两个范围或容器并返回一个tuplepair多个数据范围的操作。

现在我们可以修好你的了。升级()以使用完美的转发和auto->decltype返回值。但我不会打扰:std算法有它们的位置,但for_each很少值得。

于 2013-07-21T21:31:02.820 回答
0

我基于 for_each 做了这个小算法:

namespace estd {

template<class InputIt, class BinaryFunction>
BinaryFunction for_each_with_index(
    InputIt first, InputIt last, BinaryFunction f)
{
  for (auto index = 0; first != last; ++first, ++index) {
    f(*first, index);
  }
  return f;
}

}

然后我就可以写一个像这样的简单例子:

estd::for_each_with_index(
    begin(strings_),
    end(strings_),
    [&sx, &sy](const auto& s, auto index) {
       // Do something with my string and integer index
});

estd::for_each_with_index 将适用于任何前向迭代器(任何提供 *、++、!= 的东西)。

于 2016-03-21T11:45:45.563 回答