0

i wanna do this, but i think set::iterator does not return reference of element (just value of element)

inside of deque list

front {A - B - C - D - E - F - G} back

i get element 'C' by this code

book& temp = list[2];

and i insert this inside of my set set_list

set_list.insert(temp);

and in the same local function, i do this

for (set<book>::iterator iter = set_list.begin(); iter != set_list.end(); iter++) {
  if(*iter == C) // just for example, i can prove this is object C or not
    const_cast<book&>((*iter).setName('T');
}

when i debug my program, i can found that name of 'C' in the set_list is updated to 'T' well, but

name of 'C' in the 'list'(deque) is not updated to 'T'.

how can i solve this?

thx.

--- edit --- comparison operator

    bool operator < (const book& v) const {
      if (y == v.y) {
        return x < v.x;
      }
      else {
        return y < v.y;
      }
    }
seenblee
  • 52
  • 4
  • Show us the comparison operator `<` for your `book` class. – John Zwinck Nov 29 '20 at 08:11
  • 'comparison operator <' means this? bool operator < (const book& v) const { if (y == v.y) { return x < v.x; } else { return y < v.y; } } – seenblee Nov 29 '20 at 08:12
  • 2
    When you `insert` the variable `temp` it's a copy, and that's what gets modified. – cigien Nov 29 '20 at 08:14
  • i wanna insert 'temp' with reference to solve my problem. is it possible with ? if not, how can i solve my problem? is there a way to find element 'temp' in 'list' with copy? – seenblee Nov 29 '20 at 08:16
  • You should post a complete program we can run. The fragments you've posted so far are not sufficient to address your questions. – John Zwinck Nov 29 '20 at 08:36
  • `set` initializes its elements using the default constructor of `book` for allocation and assigns the value you insert via copy constructor. You may work around this by using a `set`, but any modification of the books stored in such a set that changes the order as defined by `MyBookCompare` will render the `set` unuseable, since it relies on the sorting... – fabian Nov 29 '20 at 08:52
  • `const_cast((*iter).setName('T')` exhibits undefined behavior, by way of modifying a `const` object. – Igor Tandetnik Nov 29 '20 at 12:56
  • This strikes me as an XY problem. I think we can probably give you better help if you step back from the mechanics of sets and deques and such, and tell us what you're really trying to accomplish. – Jerry Coffin Nov 29 '20 at 20:59

1 Answers1

0

First of all, this code:

set_list.insert(temp);

doesn't store a reference in your set even though you are using a reference to an object in your std::deque. A copy of the object is made when you insert into the set because STL containers don't allow you to store references. However, you can store pointers in them, and with pointers you avoid the copies being made in the two containers.

The other issue you've got is that the iterator that std::set returns, for both set<T>::const_iterator and set<T>::iterator, is a const iterator. There's a reason for this: it is an ordered container. Changing the elements can affect the ordering, rendering the std::set invalid. Internally it's implemented as a red-black tree and it wouldn't know of changes you make to keys. That's why you shouldn't do it.

However, in your case it looks like the name field of each book element is not part of the key. So it's possible to update that without affecting the ordering.

To overcome both issues, you could change the set to be of type:

std::set<std::shared_ptr<book>, CompareBook>

where CompareBook is a custom comparator. Using a shared pointer, you can refer to the same book object in both your deque and set.

You could also make the comparator transparent, meaning that you could search for book by the something that is semantically a key, even if it is not technically a key. In your case the x and y values. To make things easier, you could define a struct for the key that encapsulates these two fields:

struct Point {
    int x;
    int y;

    bool operator < (const Point& v) const {
      if (y == v.y) {
        return x < v.x;
      }
      else {
        return y < v.y;
      }
    }
};

struct book
{
    book(int x, int y, std::string name): point{x, y}, name{name} {}

    Point point;
    std::string name;
};

Putting all this together, you would have a comparator like this:

struct CompareBook
{
    using is_transparent = void; //this typedef allows you to search by something other than the key

    bool operator()(const std::shared_ptr<book>& book1, const std::shared_ptr<book>& book2) const
    {
        return book1->point < book2->point;
    }

    bool operator()(const Point &point, const std::shared_ptr<book>& book) const
    {
        return point < book->point;
    }

    bool operator()(const std::shared_ptr<book>& book, const Point &point) const
    {
        return book->point < point;
    }
};

And you would use it like this:

int main()
{
    std::set<std::shared_ptr<book>, CompareBook> set_list;
    std::deque<std::shared_ptr<book>> list;

    std::shared_ptr<book> book1 = std::make_shared<book>(1, 1, std::string("Hello Kitty"));
    std::shared_ptr<book> book2 = std::make_shared<book>(1, 2, std::string("C++ Bible"));

    list.push_back(book1);
    list.push_back(book2);

    set_list.insert(book1);
    set_list.insert(book2);

    auto it = set_list.find(Point{1,1});
    if (it != set_list.end())
    {
        std::cout << (*it)->name;
    }

    return 0;
}

Here's a demo.

Final word of caution: even though you can access the same element in the deque and set via the pointer, you should avoid changing the fields that take part in ordering, i.e. the x and y, to avoid messing up your set.

Dharman
  • 30,962
  • 25
  • 85
  • 135
jignatius
  • 6,304
  • 2
  • 15
  • 30
  • thank you very much :D. I used 'reference_wrapper' to insert reference of book, but your answer is also helpful for my problem. thank you for answer my question!! – seenblee Nov 30 '20 at 00:22