Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Having a vector of weak_ptr, want to return a vector of shared_ptr

I'm currently working on a big project and I need to use weak_ptr instead of shared_ptr.

Here is my problem.

I have a class named House with an attribute: vector<boost::shared_ptr<People>> my_people. I want to modify this data member to be vector<boost::weak_ptr<People>> my_people.

My getter was

vector<boost::shared_ptr<People>>& getPeople() const
{
    return my_people;
}

Normally, with a simple weak_ptr I can return my_people.lock();

But I have a vector and I don't know how to do something like this:

vector<boost::shared_ptr<People>>& getPeople() const
{
    for( vector<boost::weak_ptr<People>::iterator it = my_people.begin();
         it != my_people.end();
         ++it)
    {
        (*it).lock();
    }

    return my_people;
}

In other words, I want to return my vector of weak_ptr but as a vector of shared_ptr. Is it possible? Or do I have to return a vector of weak_ptr and use lock() everywhere I use them?

like image 729
blackmesa Avatar asked Oct 15 '12 12:10

blackmesa


3 Answers

Your function is a reasonable start:

vector<boost::shared_ptr<People>>& getPeople() const
{
    for( vector<boost::weak_ptr<People>::iterator it = my_people.begin();
         it != my_people.end();
         ++it)
    {
        (*it).lock();
    }

    return my_people;
}

But calling (*it).lock() just creates a shared_ptr and throws it away, it doesn't change the type of the vector elements, and you can't return the vector as a different type.

You need to create a vector of the right type, fill it with the shared_ptr objects, and return it:

vector<boost::shared_ptr<People>> getPeople() const
{
    vector<boost::shared_ptr<People>> people(my_people.size());
    std::transform(my_people.begin(), my_people.end(), people.begin(),
                   boost::bind(&boost::weak_ptr<People>::lock, _1));
    return people;
}

This iterates over each element of my_people, calls lock() on it, and assigns the result to the corresponding element of people.

If you know that my_people never contains expired pointers it's even easier:

vector<boost::shared_ptr<People>> getPeople() const
{
  vector<boost::shared_ptr<People>> people(my_people.begin(), my_people.end());
  return people;
}

This fills the people vector by constructing each shared_ptr element from a weak_ptr element. The difference is that this version will throw an exception if a weak_ptr has expired because the shared_ptr constructor throws if passed an expired weak_ptr. The version usingtransform will put an empty shared_ptr in the vector if an expired weak_ptr is transformed.

like image 103
Jonathan Wakely Avatar answered Oct 01 '22 20:10

Jonathan Wakely


You could use std::transform

 std::vector<std::shared_ptr<People>> temp;
 sharedTargetList.resize(my_people.size());

 //transform into a shared_ptr vector
 std::transform(my_people.begin(),
        my_people.end(),
        temp.begin(), 
        [](std::weak_ptr<People> weakPtr){ return weakPtr.lock(); } 
 );
like image 44
Yochai Timmer Avatar answered Oct 01 '22 21:10

Yochai Timmer


What about:

vector<boost::shared_ptr<People>> getPeople() const
{
    vector<boost::shared_ptr<People>> res;
    for( vector<boost::weak_ptr<People>::iterator it = my_people.begin(); 
         it != my_people.end(); ++it)
        res.push_back(it->lock());
    return res;
}

Also, you can filter out the null pointers, if you want.

Of course, you cannot return a reference to a local variable so you have to return a copy. You may want to do instead:

void getPeople(vector<boost::shared_ptr<People>> &res) const
{
    for( vector<boost::weak_ptr<People>::iterator it = my_people.begin(); 
         it != my_people.end(); ++it)
        res.push_back(it->lock());
}

to avoid copying the return vector.

like image 38
rodrigo Avatar answered Oct 01 '22 20:10

rodrigo