使std :: for_each更有用 - 这对于函子知道当前索引是个好主意吗?

时间:2013-07-21 21:19:10

标签: c++ c++11 functor

通常我不能使用std :: for_each,因为我对特定元素的逻辑取决于它的当前索引。为此,我发明了一个函数类,它包装了主函数并将其传递给当前索引。理想情况下,我想将它与lambda表达式一起使用。我创造的课程安全有效吗?还有更好的解决方案吗?我确实希望包装器的operator()返回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 个答案:

答案 0 :(得分:4)

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

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;
}

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

答案 1 :(得分: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;
});

答案 2 :(得分:3)

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

即便如此,我只需使用基于范围的range_view循环的template for( : )range_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是获取两个范围或容器并返回范围超过tuple的操作或pair的数据。

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

答案 3 :(得分: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
});

\ testd_for_each_with_index将适用于任何前向迭代器(任何提供*,++,!=的东西)。