Reputation: 652
Here is my problem: sometimes I have an std::string defined in a function. I need to extend the scope of the string because of an asynchronous operation. Naturally I can copy it into a shared pointer like this:
{
std::string hi{"hi"};
auto shared_hi = std::make_shared<std::string>(hi);
//insert shared_hi in async queue
}
The problem with this is that sometimes my strings are very very very big, and sometimes they are vectors, and sometimes they are std::arrays. so not only would I like to avoid a copy, but I would also like to have a function that can "steal" the data out of containers without having to copy them. I've come up with a clever solution posted below, but I'm wondering if there isn't a better solution to this. If there isn't, I'd like to know if doing what I'm doing below is defined behavior:
template<class T>
class WrappedDeleter {
public:
WrappedDeleter(T &&other): o_(std::move(other)) {
}
private:
T o_;
};
//This function creates a deleter where the scope
template<class P, class T>
std::function<void(P *)> make_delete(T &&encapsulate) {
WrappedDeleter<T> *d = new WrappedDeleter<T>(std::move(encapsulate));
return [d](P * ptr) {
std::cout << "deleting now\n";
delete d;
};
}
template<class P, class C>
std::shared_ptr<P> steal_data(C &&data) {
P *ptr = data.data();
//can't use make_shared - custom deleter
return std::shared_ptr<P>(ptr, make_delete<P, C>(std::move(data)));
}
used like this:
int main() {
{
std::shared_ptr<int> p_a;
std::shared_ptr<int> p_b;
std::shared_ptr<const char> p_c;
{
std::array<int,3> a= {{1,2,3}};
std::vector<int> b= {1,2,3};
std::string c= {"hello world"};
p_a = steal_data<int,std::array<int,3> >(std::move(a));
p_b = steal_data<int, std::vector<int> >(std::move(b));
p_c = steal_data<const char, std::string>(std::move(c));
std::cout << "inside" << *p_a << " " << *p_b << " " << p_c.get() << std::endl;
}
std::cout << "outside" << *p_a << " " << *p_b << " " << p_c.get() << std::endl;
std::cout << "end of scope\n";
}
return 0;
}
Upvotes: 2
Views: 229
Reputation: 157414
As Praetorian says, the only sensible way to move data into a shared_ptr
is using make_shared<T>(move(obj))
. If you want the shared_ptr
to point to the underlying contiguous data block rather than the container itself, you can use the alias constructor template<class Y> shared_ptr(const shared_ptr<Y>& r, T *ptr);
:
std::vector<int> v{1, 2, 3};
auto cptr = std::make_shared<std::vector<int>>(std::move(v));
std::shared_ptr<int> ptr{cptr, cptr->data()};
As a function:
template<typename Container>
std::shared_ptr<Container::value_type> steal_data(Container &&cont) {
auto cptr = std::make_shared<Container>(std::move(cont));
return {cptr, cptr->data()};
}
Upvotes: 2