如何使用 for_each 获取向量中值的索引?

发布于 2024-09-24 00:58:04 字数 382 浏览 4 评论 0原文

我有以下代码(编译器:MSVC++ 10):

std::vector<float> data;
data.push_back(1.0f);
data.push_back(1.0f);
data.push_back(2.0f);

// lambda expression
std::for_each(data.begin(), data.end(), [](int value) {
     // Can I get here index of the value too?
});

在上面的代码片段中我想要的是获取 lambda 表达式内的数据向量中的值的索引。看来 for_each 只接受单个参数函数。使用 for_each 和 lambda 有什么替代方案吗?

I have the following code (compiler: MSVC++ 10):

std::vector<float> data;
data.push_back(1.0f);
data.push_back(1.0f);
data.push_back(2.0f);

// lambda expression
std::for_each(data.begin(), data.end(), [](int value) {
     // Can I get here index of the value too?
});

What I want in the above code snippet is to get the index of the value in the data vector inside the lambda expression. It seems for_each only accepts a single parameter function. Is there any alternative to this using for_each and lambda?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

我三岁 2024-10-01 00:58:04

或者,您可以使用 &value - &data[0],尽管它可能会贵一些。

std::for_each(data.begin(), data.end(), [&data](float const& value) {
    int idx = &value - &data[0];
});

Alternatively, you can use &value - &data[0], although it might be a bit more expensive.

std::for_each(data.begin(), data.end(), [&data](float const& value) {
    int idx = &value - &data[0];
});
十雾 2024-10-01 00:58:04

在 C++14 中,感谢 广义 lambda 捕获,您可以执行类似的操作所以:

std::vector<int> v(10);
std::for_each(v.begin(), v.end(), [idx = 0] (int i) mutable {
    // your code...
    ++idx; // 0, 1, 2... 9
});

In C++14 thanks to generalized lambda captures you can do something like so:

std::vector<int> v(10);
std::for_each(v.begin(), v.end(), [idx = 0] (int i) mutable {
    // your code...
    ++idx; // 0, 1, 2... 9
});
冷默言语 2024-10-01 00:58:04

我认为您无法捕获索引,但您可以使用外部变量来进行索引,将其捕获到 lambda 中:

int j = 0;
std::for_each(data.begin(), data.end(), [&j](float const& value) {
            j++;
});
std::cout << j << std::endl;

这会按预期打印 3,并且 j 保存指数。

如果你想要实际的迭代器,你也许可以这样做:

std::vector<float>::const_iterator it = data.begin();
std::for_each(data.begin(), data.end(), [&it](float const& value) {
            // here "it" has the iterator
            ++it; 
});

I don't think you can capture the index, but you can use an outer variable to do the indexing, capturing it into the lambda:

int j = 0;
std::for_each(data.begin(), data.end(), [&j](float const& value) {
            j++;
});
std::cout << j << std::endl;

This prints 3, as expected, and j holds the value of the index.

If you want the actual iterator, you maybe can do it similarly:

std::vector<float>::const_iterator it = data.begin();
std::for_each(data.begin(), data.end(), [&it](float const& value) {
            // here "it" has the iterator
            ++it; 
});
时光无声 2024-10-01 00:58:04

像这样的东西:

template <typename IteratorT, typename FunctionT>
FunctionT enumerate(IteratorT first, 
                    IteratorT last, 
                    typename std::iterator_traits<IteratorT>::difference_type initial,
                    FunctionT func)
{
    for (;first != last; ++first, ++initial)
        func(initial, *first);
    return func;
}

用作:

enumerate(data.begin(), data.end(), 0, [](unsigned index, float val)
{
    std::cout << index << " " << val << std::endl;
});

Something like this:

template <typename IteratorT, typename FunctionT>
FunctionT enumerate(IteratorT first, 
                    IteratorT last, 
                    typename std::iterator_traits<IteratorT>::difference_type initial,
                    FunctionT func)
{
    for (;first != last; ++first, ++initial)
        func(initial, *first);
    return func;
}

Used as:

enumerate(data.begin(), data.end(), 0, [](unsigned index, float val)
{
    std::cout << index << " " << val << std::endl;
});
月寒剑心 2024-10-01 00:58:04

我认为最简单的方法是使用 std::accumulate:

std::accumulate(data.begin(), data.end(), 0, [](int index, float const& value)->int{
    ...
    return index + 1;
});

此解决方案适用于任何容器,并且不需要变量或自定义类。

I think that the simplest way is to use std::accumulate:

std::accumulate(data.begin(), data.end(), 0, [](int index, float const& value)->int{
    ...
    return index + 1;
});

This solution works with any container and it don't require a variable or custom classes.

小嗷兮 2024-10-01 00:58:04

另一种方法 用于枚举的包装迭代器

必需的标头:

#include <algorithm>
#include <iterator>
#include <utility>

包装迭代器:

template<class Iter, class Offset=int>
struct EnumerateIterator : std::iterator<std::input_iterator_tag, void, void, void, void> {
  Iter base;
  Offset n;

  EnumerateIterator(Iter base, Offset n = Offset()) : base (base), n (n) {}

  EnumerateIterator& operator++() { ++base; ++n; return *this; }
  EnumerateIterator operator++(int) { auto copy = *this; ++*this; return copy; }

  friend bool operator==(EnumerateIterator const& a, EnumerateIterator const& b) {
    return a.base == b.base;
  }
  friend bool operator!=(EnumerateIterator const& a, EnumerateIterator const& b) {
    return !(a == b);
  }

  struct Pair {
    Offset first;
    typename std::iterator_traits<Iter>::reference second;

    Pair(Offset n, Iter iter) : first (n), second(*iter) {}

    Pair* operator->() { return this; }
  };

  Pair operator*() { return Pair(n, base); }
  Pair operator->() { return Pair(n, base); }
};

枚举重载:

template<class Iter, class Func>
Func enumerate(Iter begin, Iter end, Func func) {
  typedef EnumerateIterator<Iter> EI;
  return std::for_each(EI(begin), EI(end), func);
}
template<class T, int N, class Func>
Func enumerate(T (&a)[N], Func func) {
  return enumerate(a, a + N, func);
}
template<class C, class Func>
Func enumerate(C& c, Func func) {
  using std::begin;
  using std::end;
  return enumerate(begin(c), end(c), func);
}

从 James 复制的测试:

#include <array>
#include <iostream>

struct print_pair {
  template<class Pair>
  void operator()(Pair const& p) {
    std::cout << p.first << ": " << p.second << "\n";
  }
};

int main() {
  std::array<float, 5> data = {1, 3, 5, 7, 9};
  enumerate(data, print_pair());
  return 0;
}

我不包括在此处提供偏移量;尽管 EnumerateIterator 中已完全准备好从 0 以外的位置开始。剩下的选择是进行偏移量的类型以及是否为额外参数添加重载或使用默认值。 (没有理由偏移量必须是迭代器的差异类型,例如,如果将其设置为某种与日期相关的类型,并且每次迭代对应于第二天会怎么样?)

Another way to wrap iterators for enumerate:

Required headers:

#include <algorithm>
#include <iterator>
#include <utility>

Wrapping iterator:

template<class Iter, class Offset=int>
struct EnumerateIterator : std::iterator<std::input_iterator_tag, void, void, void, void> {
  Iter base;
  Offset n;

  EnumerateIterator(Iter base, Offset n = Offset()) : base (base), n (n) {}

  EnumerateIterator& operator++() { ++base; ++n; return *this; }
  EnumerateIterator operator++(int) { auto copy = *this; ++*this; return copy; }

  friend bool operator==(EnumerateIterator const& a, EnumerateIterator const& b) {
    return a.base == b.base;
  }
  friend bool operator!=(EnumerateIterator const& a, EnumerateIterator const& b) {
    return !(a == b);
  }

  struct Pair {
    Offset first;
    typename std::iterator_traits<Iter>::reference second;

    Pair(Offset n, Iter iter) : first (n), second(*iter) {}

    Pair* operator->() { return this; }
  };

  Pair operator*() { return Pair(n, base); }
  Pair operator->() { return Pair(n, base); }
};

Enumerate overloads:

template<class Iter, class Func>
Func enumerate(Iter begin, Iter end, Func func) {
  typedef EnumerateIterator<Iter> EI;
  return std::for_each(EI(begin), EI(end), func);
}
template<class T, int N, class Func>
Func enumerate(T (&a)[N], Func func) {
  return enumerate(a, a + N, func);
}
template<class C, class Func>
Func enumerate(C& c, Func func) {
  using std::begin;
  using std::end;
  return enumerate(begin(c), end(c), func);
}

Copied test from James:

#include <array>
#include <iostream>

struct print_pair {
  template<class Pair>
  void operator()(Pair const& p) {
    std::cout << p.first << ": " << p.second << "\n";
  }
};

int main() {
  std::array<float, 5> data = {1, 3, 5, 7, 9};
  enumerate(data, print_pair());
  return 0;
}

I don't include providing an offset here; though it's fully ready in EnumerateIterator to start at otherwise than 0. The choice left is what type to make the offset and whether to add overloads for the extra parameter or use a default value. (No reason the offset has to be the iterator's difference type, e.g. what if you made it some date related type, with each iteration corresponding to the next day?)

谜兔 2024-10-01 00:58:04

罗杰·佩特(Roger Pate)在对我的另一个答案的评论中建议创建一个执行枚举的迭代器包装器。实施起来有点困难。

此迭代器包装器采用值类型为 T 的前向迭代器(称为“内部迭代器”),并将其转换为值类型为pair的前向迭代器。 ,其中 int 是内部迭代器的距离类型。

这将非常简单,除了两件事:

  • std::pair 构造函数通过 const 引用获取其参数,因此我们无法初始化 T& 类型的数据成员;我们必须为迭代器创建我们自己的对类型。
  • 为了支持迭代器的正确语义,我们需要一个左值(operator*需要返回一个引用,operator->需要返回一个指针),所以该对必须是迭代器的数据成员。由于它包含引用,我们需要一种方法来“重置”它,并且需要延迟初始化它,以便我们可以正确处理结束迭代器。如果 T 不可分配, boost::optional 似乎不喜欢它,因此我们将编写自己的简单 lazy >。

lazy 包装器:

#include <new>
#include <type_traits>

// A trivial lazily-initialized object wrapper; does not support references
template<typename T>
class lazy
{
public:

    lazy() : initialized_(false) { }
    lazy(const T& x) : initialized_(false) { construct(x); }

    lazy(const lazy& other)
        : initialized_(false)
    {
        if (other.initialized_)
            construct(other.get());
    }

    lazy& operator=(const lazy& other)
    {
        // To the best of my knowledge, there is no clean way around the self
        // assignment check here since T may not be assignable
        if (this != &other)
            construct(other.get());
        return *this;
    }

    ~lazy() { destroy(); }

    void reset() { destroy(); }
    void reset(const T& x) { construct(x); }

          T& get()       { return reinterpret_cast<      T&>(object_); }
    const T& get() const { return reinterpret_cast<const T&>(object_); }

private:

    // Ensure lazy<T> is not instantiated with T as a reference type
    typedef typename std::enable_if<
        !std::is_reference<T>::value
    >::type ensure_t_is_not_a_reference;

    void construct(const T& x) 
    {
        destroy();
        new (&object_) T(x); 
        initialized_ = true;
    }

    void destroy() 
    { 
        if (initialized_)
            reinterpret_cast<T&>(object_).~T();
        initialized_ = false;
    }

    typedef typename std::aligned_storage<
        sizeof T, 
        std::alignment_of<T>::value
    >::type storage_type;

    storage_type object_;
    bool initialized_;
};

enumerate_iterator

#include <iterator>
#include <type_traits>

// An enumerating iterator that transforms an iterator with a value type of T
// into an iterator with a value type of pair<index, T&>.
template <typename IteratorT>
class enumerating_iterator
{
public:

    typedef IteratorT                              inner_iterator;
    typedef std::iterator_traits<IteratorT>        inner_traits;
    typedef typename inner_traits::difference_type inner_difference_type;
    typedef typename inner_traits::reference       inner_reference;

    // A stripped-down version of std::pair to serve as a value type since
    // std::pair does not like having a reference type as a member.
    struct value_type
    {
        value_type(inner_difference_type f, inner_reference s)
            : first(f), second(s) { }

        inner_difference_type first;
        inner_reference       second;
    };

    typedef std::forward_iterator_tag iterator_category;
    typedef inner_difference_type     difference_type;
    typedef value_type&               reference;
    typedef value_type*               pointer;

    explicit enumerating_iterator(inner_iterator it = inner_iterator(), 
                                  difference_type index = 0) 
        : it_(it), index_(index) { }

    enumerating_iterator& operator++() 
    {
        ++index_;
        ++it_;
        return *this;
    }

    enumerating_iterator operator++(int)
    {
        enumerating_iterator old_this(*this);
        ++*this;
        return old_this;
    }

    const value_type& operator*() const 
    { 
        value_.reset(value_type(index_, *it_));
        return value_.get();
    }

    const value_type* operator->() const { return &**this; }

    friend bool operator==(const enumerating_iterator& lhs,
                           const enumerating_iterator& rhs)
    {
        return lhs.it_ == rhs.it_;
    }

    friend bool operator!=(const enumerating_iterator& lhs,
                           const enumerating_iterator& rhs)
    {
        return !(lhs == rhs);
    }

private:

    // Ensure that the template argument passed to IteratorT is a forward
    // iterator; if template instantiation fails on this line, IteratorT is
    // not a valid forward iterator:
    typedef typename std::enable_if<
        std::is_base_of<
            std::forward_iterator_tag,
            typename std::iterator_traits<IteratorT>::iterator_category
        >::value
    >::type ensure_iterator_t_is_a_forward_iterator;

    inner_iterator it_;              //< The current iterator
    difference_type index_;          //< The index at the current iterator
    mutable lazy<value_type> value_; //< Pair to return from op* and op->
};

// enumerating_iterator<T> construction type deduction helpers
template <typename IteratorT>
enumerating_iterator<IteratorT> make_enumerator(IteratorT it)
{
    return enumerating_iterator<IteratorT>(it);
}

template <typename IteratorT, typename DifferenceT>
enumerating_iterator<IteratorT> make_enumerator(IteratorT it, DifferenceT idx)
{
    return enumerating_iterator<IteratorT>(it, idx);
}

测试存根:

#include <algorithm>
#include <array>
#include <iostream>

struct print_pair
{
    template <typename PairT> 
    void operator()(const PairT& p)
    {
        std::cout << p.first << ": " << p.second << std::endl;
    }
};

int main()
{
    std::array<float, 5> data = { 1, 3, 5, 7, 9 };

    std::for_each(make_enumerator(data.begin()), 
                  make_enumerator(data.end()), 
                  print_pair());
}

这已经过最低限度的测试;如果我删除 C++0x 类型特征和aligned_storage,Comeau 和 g++ 4.1 都会接受它(我在这台笔记本电脑上没有更新版本的 g++ 进行测试)。如果您发现任何错误,请告诉我。

我对有关如何改进这一点的建议非常感兴趣。具体来说,我很想知道是否有办法绕过必须使用 lazy,可以通过使用 Boost 中的某些内容,也可以通过修改迭代器本身。我希望我只是愚蠢,实际上有一种非常简单的方法可以更干净地实现这一点。

Roger Pate suggested in a comment to my other answer creating an iterator wrapper that performs the enumeration. Implementing it was a bit of a beating.

This iterator wrapper takes a forward iterator whose value type is T (called the "inner iterator") and transforms it into a forward iterator whose value type is a pair<int, T&>, where int is the distance type of the inner iterator.

This would be quite simple, except for two things:

  • The std::pair constructor takes its arguments by const reference so we can't initialize a data member of type T&; we'll have to create our own pair type for the iterator.
  • In order to support the correct semantics for the iterator, we need an lvalue (operator* needs to return a reference and operator-> needs to return a pointer), so the pair needs to be a data member of the iterator. Since it contains a reference, we'll need a way to "reset" it and we'll need it to be lazily initialized so that we can correctly handle end iterators. boost::optional<T> seems not to like it if T is not assignable, so we'll write our own simple lazy<T>.

The lazy<T> wrapper:

#include <new>
#include <type_traits>

// A trivial lazily-initialized object wrapper; does not support references
template<typename T>
class lazy
{
public:

    lazy() : initialized_(false) { }
    lazy(const T& x) : initialized_(false) { construct(x); }

    lazy(const lazy& other)
        : initialized_(false)
    {
        if (other.initialized_)
            construct(other.get());
    }

    lazy& operator=(const lazy& other)
    {
        // To the best of my knowledge, there is no clean way around the self
        // assignment check here since T may not be assignable
        if (this != &other)
            construct(other.get());
        return *this;
    }

    ~lazy() { destroy(); }

    void reset() { destroy(); }
    void reset(const T& x) { construct(x); }

          T& get()       { return reinterpret_cast<      T&>(object_); }
    const T& get() const { return reinterpret_cast<const T&>(object_); }

private:

    // Ensure lazy<T> is not instantiated with T as a reference type
    typedef typename std::enable_if<
        !std::is_reference<T>::value
    >::type ensure_t_is_not_a_reference;

    void construct(const T& x) 
    {
        destroy();
        new (&object_) T(x); 
        initialized_ = true;
    }

    void destroy() 
    { 
        if (initialized_)
            reinterpret_cast<T&>(object_).~T();
        initialized_ = false;
    }

    typedef typename std::aligned_storage<
        sizeof T, 
        std::alignment_of<T>::value
    >::type storage_type;

    storage_type object_;
    bool initialized_;
};

The enumerating_iterator:

#include <iterator>
#include <type_traits>

// An enumerating iterator that transforms an iterator with a value type of T
// into an iterator with a value type of pair<index, T&>.
template <typename IteratorT>
class enumerating_iterator
{
public:

    typedef IteratorT                              inner_iterator;
    typedef std::iterator_traits<IteratorT>        inner_traits;
    typedef typename inner_traits::difference_type inner_difference_type;
    typedef typename inner_traits::reference       inner_reference;

    // A stripped-down version of std::pair to serve as a value type since
    // std::pair does not like having a reference type as a member.
    struct value_type
    {
        value_type(inner_difference_type f, inner_reference s)
            : first(f), second(s) { }

        inner_difference_type first;
        inner_reference       second;
    };

    typedef std::forward_iterator_tag iterator_category;
    typedef inner_difference_type     difference_type;
    typedef value_type&               reference;
    typedef value_type*               pointer;

    explicit enumerating_iterator(inner_iterator it = inner_iterator(), 
                                  difference_type index = 0) 
        : it_(it), index_(index) { }

    enumerating_iterator& operator++() 
    {
        ++index_;
        ++it_;
        return *this;
    }

    enumerating_iterator operator++(int)
    {
        enumerating_iterator old_this(*this);
        ++*this;
        return old_this;
    }

    const value_type& operator*() const 
    { 
        value_.reset(value_type(index_, *it_));
        return value_.get();
    }

    const value_type* operator->() const { return &**this; }

    friend bool operator==(const enumerating_iterator& lhs,
                           const enumerating_iterator& rhs)
    {
        return lhs.it_ == rhs.it_;
    }

    friend bool operator!=(const enumerating_iterator& lhs,
                           const enumerating_iterator& rhs)
    {
        return !(lhs == rhs);
    }

private:

    // Ensure that the template argument passed to IteratorT is a forward
    // iterator; if template instantiation fails on this line, IteratorT is
    // not a valid forward iterator:
    typedef typename std::enable_if<
        std::is_base_of<
            std::forward_iterator_tag,
            typename std::iterator_traits<IteratorT>::iterator_category
        >::value
    >::type ensure_iterator_t_is_a_forward_iterator;

    inner_iterator it_;              //< The current iterator
    difference_type index_;          //< The index at the current iterator
    mutable lazy<value_type> value_; //< Pair to return from op* and op->
};

// enumerating_iterator<T> construction type deduction helpers
template <typename IteratorT>
enumerating_iterator<IteratorT> make_enumerator(IteratorT it)
{
    return enumerating_iterator<IteratorT>(it);
}

template <typename IteratorT, typename DifferenceT>
enumerating_iterator<IteratorT> make_enumerator(IteratorT it, DifferenceT idx)
{
    return enumerating_iterator<IteratorT>(it, idx);
}

A test stub:

#include <algorithm>
#include <array>
#include <iostream>

struct print_pair
{
    template <typename PairT> 
    void operator()(const PairT& p)
    {
        std::cout << p.first << ": " << p.second << std::endl;
    }
};

int main()
{
    std::array<float, 5> data = { 1, 3, 5, 7, 9 };

    std::for_each(make_enumerator(data.begin()), 
                  make_enumerator(data.end()), 
                  print_pair());
}

This has been minimally tested; Comeau and g++ 4.1 both accept it if I remove the C++0x type traits and aligned_storage (I don't have a newer version of g++ on this laptop to test with). Please let me know if you find any bugs.

I'm very interested in suggestions about how to improve this. Specifically, I'd love to know if there is a way around having to use lazy<T>, either by using something from Boost or by modifying the iterator itself. I hope I'm just being dumb and that there's actually a really easy way to implement this more cleanly.

浪菊怪哟 2024-10-01 00:58:04

遵循 C 和 C++ 的标准约定,第一个元素的索引为 0,最后一个元素的索引为 size() - 1。

因此,您必须执行以下操作;-

std::vector<float> data;
int index = 0;

data.push_back(1.0f);
data.push_back(1.0f);
data.push_back(2.0f);

// lambda expression
std::for_each(data.begin(), data.end(), [&index](float value) {
// Can I get here index of the value too?
   cout<<"Current Index :"<<index++; // gets the current index before increment
});

Following the standard convention for C and C++, the first element has index 0, and the last element has index size() - 1.

So you have to do the following;-

std::vector<float> data;
int index = 0;

data.push_back(1.0f);
data.push_back(1.0f);
data.push_back(2.0f);

// lambda expression
std::for_each(data.begin(), data.end(), [&index](float value) {
// Can I get here index of the value too?
   cout<<"Current Index :"<<index++; // gets the current index before increment
});
绿萝 2024-10-01 00:58:04

您还可以将结构作为第三个参数传递给 std::for_each 并计算其中的索引,如下所示:

struct myStruct {
   myStruct(void) : index(0) {};
   void operator() (float i) { cout << index << ": " << i << endl; index++; }
   int index;
};

int main()
{

   std::vector data;
   data.push_back(1.0f);
   data.push_back(4.0f);
   data.push_back(8.0f);

   // lambda expression
   std::for_each(data.begin(), data.end(), myStruct());

   return 0;
}

You could also pass a struct as third argument to std::for_each and count the index in it like so:

struct myStruct {
   myStruct(void) : index(0) {};
   void operator() (float i) { cout << index << ": " << i << endl; index++; }
   int index;
};

int main()
{

   std::vector data;
   data.push_back(1.0f);
   data.push_back(4.0f);
   data.push_back(8.0f);

   // lambda expression
   std::for_each(data.begin(), data.end(), myStruct());

   return 0;
}
夜无邪 2024-10-01 00:58:04

也许在 lambda 函数中,向其传递一个 int& 而不是值 int,这样您就可以获得地址。 &那么你可以用它来从第一项中推断出你的位置,

这行得通吗?不知道for_each是否支持引用

Maybe in the lambda function, pass it a int& instead of value int, so you'd have the address. & then you could use that to deduce your position from the first item

Would that work? I don't know if for_each supports references

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文