Home > Software design >  Returning std::vector<std::unique_ptr<T>> from function
Returning std::vector<std::unique_ptr<T>> from function

Time:10-21

I'm trying to return a vector<unique_ptr> from a function, but I'm running into errors. I'm using MSVC 19.33.31630.

The comments to this question recommend returning by value, but this code:

std::vector<std::unique_ptr<int>> test1()
{
    std::vector<std::unique_ptr<int>> ret = { std::make_unique<int>(1) };
    return ret;
}

int main()
{
    std::vector<std::unique_ptr<int>> x = test1();
    std::unique_ptr<int>& ptrY = x.at(0);
    return 0;
}

yields this error:

Error C2280 'std::unique_ptr<int,std::default_delete<int>>::unique_ptr(const std::unique_ptr<int,std::default_delete<int>> &)': attempting to reference a deleted function

Returning by reference, as in this code:

std::vector<std::unique_ptr<int>>& test2()
{
    std::vector<std::unique_ptr<int>> ret = { std::make_unique<int>(1) };
    return ret;
}

int main()
{
    std::vector<std::unique_ptr<int>>& y = test2();
    std::cout << *(y.at(0)) << std::endl; 

    return 0;
}

yields the same error.

Why is this happening? Is the ownership of the unique_ptr not transferring properly?

CodePudding user response:

Thanks @user4581301 for answering this in the comments. As best as I can tell:

  • test2() puts the std::vector on the stack which goes out of scope.
  • test1() ends up copying the unique_ptr into an initializer_list

The solution is to return by value, and initialize the vector with push_back/emplace_back instead of an initializer_list:

std::vector<std::unique_ptr<int>> test3()
{
    std::vector<std::unique_ptr<int>> ret;
    ret.push_back(std::make_unique<int>(1));
    return ret;
}

int main()
{
    std::vector<std::unique_ptr<int>> x = test3();
    std::unique_ptr<int>& ptrY = x.at(0); 
    std::cout << *ptrY;

    return 0;
}

CodePudding user response:

std::vector<std::unique_ptr<int>> x = test1();

You're making a copy of the vector that test1 returns here, which in turn needs to copy all its content. That's what an assignment operator = does by default! Only after the copy is done, the return value of test1 will run out of scope and get destructed.

You're trying to copy unique_ptrs; can't copy a unique_ptr. So, this is correct.

Use std::move to move the values held by the vector.

Even better, don't use the assignment operator, but construct from the return value directly, which will use the move constructor (if the function returns by value).

  • Related