c++stdunique-ptrstructured-bindings

Why can't std::unique_ptr be returned after structured binding without using std::move?


When I try to compile the following code I get the error C2280. I guess the compiler is trying to copy the unique_ptr or something.

#include <memory>

std::pair<int, std::unique_ptr<int>> CreatePair()
{
    std::unique_ptr<int> my_int(new int);
    return { 1, std::move(my_int) };
}
std::unique_ptr<int> GetUinquePtr()
{
    auto [ignore, unique_ptr] = CreatePair();
    return unique_ptr; // <- Build error C2280 attempting to reference a deleted function
}
int main()
{
    auto unique_ptr = GetUinquePtr();
}

Complete error message:

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

It works if I add std::move():

std::unique_ptr<int> GetUinquePtr()
{
    auto [ignore, unique_ptr] = CreatePair();
    return std::move(unique_ptr); // <- This works
}

And it works fine if I use std::tie:

std::unique_ptr<int> GetUinquePtr()
{
    std::unique_ptr<int> unique_ptr;
    std::tie(std::ignore, unique_ptr) = CreatePair();
    return unique_ptr; // <- This works
}

So do need to explicitly type std::move after structured binding of a unique_ptr or am I doing something wrong here?


Solution

  • A structured binding creates references, and your code is more or less equivalent to this:

    std::unique_ptr<int> GetUinquePtr()
    {
        auto p = CreatePair();
        auto& ignore = p.first;
        auto& unique_ptr = p.second;
        return unique_ptr;
    }
    

    and returning the reference would create a copy.

    With the tie, it works rather like this instead:

    std::unique_ptr<int> GetUinquePtr()
    {
        std::unique_ptr<int> unique_ptr;
        unique_ptr = CreatePair().second;
        return unique_ptr;
    }