Using for_each to modify std containers (even though you shouldn't)

前端 未结 8 1159
甜味超标
甜味超标 2021-01-13 18:58

I\'m taking a self-study course for C++, learning how the Standard Library works, and I want to understand how this code that uses for_each works, particularly

相关标签:
8条回答
  • 2021-01-13 19:25

    You can't simply change the key of an element in a key-value store kind of a data structure. (In case of a set, it's only key, no value.)

    If you would, the element's position in the underlying container might need to be adjusted, based on the recomputed hash value in case of an unordered hash table, or a different sorting position in a binary search tree / ordered list.

    Therefore the API of the std::set requires you to erase and reinsert the item in this case.

    Which the answers to existing questions in this direction already state:

    • C++ STL set update is tedious: I can't change an element in place
    • How to update an existing element of std::set?
    0 讨论(0)
  • 2021-01-13 19:26

    The problem is that you are not allowed to modify elements in a std::set. If it were possible, then how would it handle something like this:

    std::set<int> my_set { 1, 2, 3 };
    int& foo = *(my_set.begin());
    foo = 2;
    

    Now there is two elements with value 2. That doesn't make sense in a set due to

    std::set is an associative container that contains a sorted set of unique objects of type Key.

    (emphasis mine)

    http://en.cppreference.com/w/cpp/container/set

    0 讨论(0)
  • 2021-01-13 19:32

    The doc for std::set (http://www.cplusplus.com/reference/set/set/) defines the std::set::iterator returned by non-const version of begin and end as :

    a bidirectional iterator to const value_type

    So, even non-const std::set::iterator returns a const object.

    0 讨论(0)
  • 2021-01-13 19:35

    Because a std::set manages the order of it's elements, it prohibits the user to change it's elements through it's iterators. Which means it's begin() and end() methods return a const_iterator. You're only allowed to read the element pointed to by that iterator, not modify it (it's const) which is what doubler() is trying to do.

    A solution would be to just use std::vector and std::sort to order it yourself:

    #include <iostream>
    #include <algorithm>
    #include <vector>
    
    class A {
        int a;
    public:
        A(int a) : a(a) {}
        int getA() const { return a; }
        void setA(int a) { this->a = a; }
        bool operator<(const A & b) const { return a<b.a; }
    };
    
    struct myprinter { 
        void operator()(const A & a) { cout << a.getA() << ", "; }  
    };
    
    struct doubler {
        void operator()(A& a) { a.setA(a.getA()*2); } // by reference!
    };
    
    int main() {
        int mynumbers[] = {8, 9, 7, 6, 4, 1};
        std::vector<A> s1(mynumbers, mynumbers+6);
        std::sort(s1.begin(), s1.end());
        std::for_each(s1.begin(), s1.end(), doubler());
        std::for_each(s1.begin(), s1.end(), myprinter());
        return 0;
    }
    
    0 讨论(0)
  • 2021-01-13 19:39

    This is a piece of cake with a lambda expression.

    #include <iostream>
    #include <algorithm>
    #include <vector>
    using namespace std;
    
    int main() {
        int mynumbers[] = {8, 9, 7, 6, 4, 1};
        vector<int> s1(mynumbers, mynumbers+6);
        sort(s1.begin(), s1.end());
        for_each(s1.begin(), s1.end(), [](int &x) { x*=2; });
        for_each(s1.begin(), s1.end(), [](int x) { cout << x << ", "; });
        return 0;
    }
    
    OUTPUT:
    2, 8, 12, 14, 16, 18,
    
    0 讨论(0)
  • 2021-01-13 19:41

    There is in fact nothing wrong with modifying elements in the functor of a for_each:

    If InputIt is a mutable iterator, f may modify the elements of the range through the dereferenced iterator

    So next we should look at the definition of set, note that since c++11 clarified this, both it's iterator and const_iterator types are: "Constant Bidirectional Iterators".

    So it is undefined behavior to modify a set with a for_each.

    0 讨论(0)
提交回复
热议问题