It looks like it is not possible without modifying the constructor boost::container::flat
.
Without modifying either class it seems that the only a hack would do it, for example using reinterpret_cast
.
The solution I found is either to use an alternative implementation of vector
or very ugly code.
Before going into my solution, I must that say that this is probably a
defect of both classes. These clases should have a set of
release()
/aquire(start, end)
functions that respectively
returns the pointer range to the data releasing the ownership and
gets the pointer range owning it from then on. An alternative could be to
have a constructor that moves from any other container that has a the
data member function.
Solution using reinterpret_cast
and a different implementation of vector
It turns out that reinterpret_cast
ing from std::vector
to boost::container::flat_set
is not possible, because the layout is not compatible.
However it is possible to reinterpret_cast from boost::container::vector
to boost::container::flat_set
out of the box (that is because they have a common implementation).
#include<cassert>
#include<boost/container/flat_set.hpp>
int main(){
boost::container::vector<double> v = {1.,2.,3.};
boost::container::flat_set<double> fs = std::move(reinterpret_cast<boost::container::flat_set<double>&>(v));
assert(v.size() == 0);
assert(*fs.find(2.) == 2.);s
assert(fs.find(4.) == fs.end());
}
So, I can replace std::vector
by boost::container::vector
and I can move data to a flat_set
.
Non-portable solution using std::vector
and ugly code
The reason the layout of std::vector
and boost::container::vector
are different is that boost::container::vector
stores metadata in this way:
class boost::container::vector{
pointer m_start;
size_type m_size;
size_type m_capacity;
}
while std::vector
(in GCC) is basically pure pointers,
class std::vector{
pointer _M_start;
pointer _M_finish;
pointer _M_end_of_storage;
}
So, my conclusion is that moving is possible only through a hack given that the implementation I use of std::vector
is not compatible with boost::container::flat_set
.
In an extreme case, one can do this (sorry if this code offends someone, the code is not portable):
template<class T>
boost::container::flat_set<T> to_flat_set(std::vector<T>&& from){
// struct dummy_vector{T* start; T* finish; T* end_storarge;}&
// dfrom = reinterpret_cast<dummy_vector&>(from);
boost::container::flat_set<T> ret;
struct dummy_flat_set{T* start; std::size_t size; std::size_t capacity;}&
dret = reinterpret_cast<dummy_flat_set&>(ret);
dret = {from.data(), from.size(), from.capacity()};
// dfrom.start = dfrom.finish = dfrom.end_storarge = nullptr;
new (&from) std::vector<T>();
return ret;
};
int main(){
std::vector<double> v = {1.,2.,3.};
boost::container::flat_set<double> fs = to_flat_set(std::move(v));
assert(v.size() == 0);
assert(*fs.find(2.) == 2.);
assert(fs.find(4.) == fs.end());
}
Note that I am not taking into account allocator issues at all. I am not sure how to handle allocators here.
In retrospect I don't mind using a form of cast
for this specific problem, because somehow I have to tell that the vector is sorted before moving to flat_set
. (The problem is that this goes to extreme because it is a reinterpret_cast
.)
However this is a secondary issue, there should be legal way to move from std::vector
to boost::container::vector
.