How to get the index of a value in a vector using for_each?

前端 未结 10 750
[愿得一人]
[愿得一人] 2020-12-28 14:02

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

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

// lambda          


        
10条回答
  •  没有蜡笔的小新
    2020-12-28 14:34

    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, 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 seems not to like it if T is not assignable, so we'll write our own simple lazy.

    The lazy wrapper:

    #include 
    #include 
    
    // A trivial lazily-initialized object wrapper; does not support references
    template
    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(object_); }
    
    private:
    
        // Ensure lazy is not instantiated with T as a reference type
        typedef typename std::enable_if<
            !std::is_reference::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(object_).~T();
            initialized_ = false;
        }
    
        typedef typename std::aligned_storage<
            sizeof T, 
            std::alignment_of::value
        >::type storage_type;
    
        storage_type object_;
        bool initialized_;
    };
    

    The enumerating_iterator:

    #include 
    #include 
    
    // An enumerating iterator that transforms an iterator with a value type of T
    // into an iterator with a value type of pair.
    template 
    class enumerating_iterator
    {
    public:
    
        typedef IteratorT                              inner_iterator;
        typedef std::iterator_traits        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::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_; //< Pair to return from op* and op->
    };
    
    // enumerating_iterator construction type deduction helpers
    template 
    enumerating_iterator make_enumerator(IteratorT it)
    {
        return enumerating_iterator(it);
    }
    
    template 
    enumerating_iterator make_enumerator(IteratorT it, DifferenceT idx)
    {
        return enumerating_iterator(it, idx);
    }
    

    A test stub:

    #include 
    #include 
    #include 
    
    struct print_pair
    {
        template  
        void operator()(const PairT& p)
        {
            std::cout << p.first << ": " << p.second << std::endl;
        }
    };
    
    int main()
    {
        std::array 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, 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.

提交回复
热议问题