Since C++17, std::set
has extract()
member function that support moving the node from the container. Here is an example code:
#include <iostream>
#include <cassert>
#include <set>
struct trace {
trace() {
std::cout << this << ":" << " construct" << std::endl;
}
~trace() {
std::cout << this << ":" << " destruct" << std::endl;
}
trace(trace& other) {
std::cout << this << ":" << " copy construct from " << &other << std::endl;
}
trace(trace&& other) {
std::cout << this << ":" << " move construct from " << &other << std::endl;
}
trace& operator=(trace const& other) {
std::cout << this << ":" << " copy assign from " << &other << std::endl;
return *this;
}
trace& operator=(trace&& other) {
std::cout << this << ":" << " move assign from " << &other << std::endl;
return *this;
}
};
inline bool operator<(trace const& lhs, trace const& rhs) {
std::cout << &lhs << " < " << &rhs << std::endl;
return &lhs < &rhs;
}
int main () {
std::set<trace> s;
s.insert(trace());
s.insert(trace());
s.insert(trace());
auto it = s.begin();
++it;
assert(s.size() == 3);
std::cout << "[[extract]]" << std::endl;
trace t = std::move(s.extract(it).value());
assert(s.size() == 2);
}
Running demo: https://wandbox.org/permlink/ZZHkZV1DUZpM3YrU
I got the following result:
0x7ffd30bbfbd0: construct
0x55edd361d2a0: move construct from 0x7ffd30bbfbd0
0x7ffd30bbfbd0: destruct
0x7ffd30bbfbc8: construct
0x7ffd30bbfbc8 < 0x55edd361d2a0
0x55edd361d2a0 < 0x7ffd30bbfbc8
0x7ffd30bbfbc8 < 0x55edd361d2a0
0x55edd361d2d0: move construct from 0x7ffd30bbfbc8
0x7ffd30bbfbc8: destruct
0x7ffd30bbfbc0: construct
0x7ffd30bbfbc0 < 0x55edd361d2a0
0x7ffd30bbfbc0 < 0x55edd361d2d0
0x55edd361d2d0 < 0x7ffd30bbfbc0
0x7ffd30bbfbc0 < 0x55edd361d2d0
0x55edd361d300: move construct from 0x7ffd30bbfbc0
0x7ffd30bbfbc0: destruct
[[extract]]
0x7ffd30bbfbb0: move construct from 0x55edd361d2d0
0x55edd361d2d0: destruct
0x7ffd30bbfbb0: destruct
0x55edd361d300: destruct
0x55edd361d2a0: destruct
After [[extract]]
, the container doesn't access to the move from object. It is fine.
I'm looking for a way to do the same thing on boost::multi_index
.
I tried the same way as the following Q&A: Move element from boost multi_index array
I tried the same approach:
namespace mi = boost::multi_index;
using mi_trace = mi::multi_index_container<
trace,
mi::indexed_by<
mi::ordered_unique<
mi::identity<trace>
>
>
>;
int main () {
mi_trace mi;
mi.insert(trace());
mi.insert(trace());
mi.insert(trace());
auto it = mi.begin();
++it;
assert(mi.size() == 3);
std::optional<trace> target;
std::cout << "[[modify]]" << std::endl;
if (mi.modify(
it,
[&](auto& e) {
target.emplace(std::move(e));
}
)
) {
std::cout << "[[erase]]" << std::endl;
mi.erase(it);
assert(mi.size() == 2);
}
}
Running demo: https://wandbox.org/permlink/eKpGDpMBbx5aRz9O
And got the following output:
[[modify]]
0x7fffe1f77c66: move construct from 0x55fdda3272e0
0x55fdda3272b0 < 0x55fdda3272e0
0x55fdda3272e0 < 0x55fdda327310
[[erase]]
0x55fdda3272e0: destruct
0x7fffe1f77c66: destruct
0x55fdda3272b0: destruct
0x55fdda327310: destruct
multi_index
container accesses moved from object (0x55fdda3272e0
) after modify lambda returned. I think that it is for reordering. modify()
doesn't know that I use modify()
with erase()
. For random_access_index
, it works well because the container doesn't need to reorder but it doesn't work for ordered_index
.
Is there any way to move an element from ordered_index
ed multi_index
?