Let's say I have movable and not copyable object and I have boost multi-index array with random_access index. I need to move my object out of array front, but I cannot find any method, that would give me rvalue/lvalue reference in documentation. I can only see front()
which gives me constant reference and pop_front()
which erases element, but does not return anything. So is there a way to move element out of boost multi-index?
Move element from boost multi_index array
725 views Asked by Slava At
2
There are 2 answers
17
On
Non-const element operations are not supported because they could leave elements in a state which would break invariants placed on them by the various indexes.
The closest thing you can do is using modify
:
moveonly pop_front(Table& table) {
moveonly extracted;
auto it = table.begin();
if (it == table.end())
throw std::logic_error("pop_front");
if (table.modify(it, [&](moveonly& v) { extracted = std::move(v); })) {
table.erase(it);
}
return extracted;
}
Note that modify
does incur the cost of checking all indexes, and may fail. Fortunately, if it does fail, the effect is that iterator
is erased:
- Effects: Calls mod(e) where e is the element pointed to by position and rearranges *position into all the indices of the multi_index_container. Rearrangement on sequenced indices does not change the position of the element with respect to the index; rearrangement on other indices may or might not succeed. If the rearrangement fails, the element is erased.
- Postconditions: Validity of position is preserved if the operation succeeds.
And here's a live demo:
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/random_access_index.hpp>
#include <iostream>
struct moveonly {
int x;
moveonly(int x = -1) noexcept : x(x) {}
moveonly(moveonly&& o) noexcept : x(o.x) { o = {}; }
moveonly& operator=(moveonly o) noexcept { using std::swap; swap(x, o.x); return *this; }
};
static_assert(not std::is_copy_constructible<moveonly>{}, "moveonly");
namespace bmi = boost::multi_index;
using Table = bmi::multi_index_container<moveonly,
bmi::indexed_by<
bmi::random_access<bmi::tag<struct _ra> >
> >;
template <typename Container>
void dump(std::ostream& os, Container const& c) {
for (auto& r: c) os << r.x << " ";
os << "\n";
}
moveonly pop_front(Table& table) {
moveonly extracted;
auto it = table.begin();
if (it == table.end())
throw std::logic_error("pop_front");
if (table.modify(it, [&](moveonly& v) { extracted = std::move(v); })) {
table.erase(it);
}
return extracted;
}
int main() {
Table table;
table.push_back({1});
table.push_back({2});
table.push_back({3});
dump(std::cout << "table before: ", table);
std::cout << "Extracted: " << pop_front(table).x << "\n";
dump(std::cout << "table after: ", table);
}
Which prints:
table before: 1 2 3
Extracted: 1
table after: 2 3
Adding to @sehe's answer, the following shows how to modify the code in case your moveable type is not default constructible:
Edited: changed code to properly deal with destruction of
*extracted
.Edited: added alternative with
std::unique_ptr
.Edited: added a second altrnative by sehe.
Live On Coliru
Same thing using
std::unique_ptr
for cleanup:Live On Coliru
Sehe provides yet another alternative based on
boost::optional
which is the most elegant of all:Live On Coliru