7

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?

Olppah
  • 790
  • 1
  • 10
  • 21
  • 1
    [Structured bindings](https://en.cppreference.com/w/cpp/language/structured_binding) creates aliases, similar to references. In your Structured Binding example, the `std::unique_ptr` copy constructor ends up be invoked. Your other examples invoke other optimizations that bypass the copy constructor. – Remy Lebeau Dec 09 '22 at 08:24
  • Please don't disguise error messages as comments in your code. Include it in the description. – molbdnilo Dec 09 '22 at 08:25
  • @molbdnilo [Compiler Error C2280](https://learn.microsoft.com/en-us/cpp/error-messages/compiler-errors-1/compiler-error-c2280?view=msvc-170): `'declaration': attempting to reference a deleted function` – Remy Lebeau Dec 09 '22 at 08:25
  • @RemyLebeau it is not due to RVO or NRVO. It is just that by default return triggers move on objects. It indeed doesn't apply to references/aliases - a copy is attempted instead. – ALX23z Dec 09 '22 at 08:26
  • @ALX23z actually, I was thinking that NRVO/copy-elison is actually the problem, causing main's `unique_ptr` to be copy-constructed directly from the original `unique_ptr` returned from `CreatePair()` – Remy Lebeau Dec 09 '22 at 08:30
  • @RemyLebeau NRVO can be applied only after compilation is done. First, the code needs to compile. And it can only compile if a move is performed, implicitly or explicitly. – ALX23z Dec 09 '22 at 08:36

1 Answers1

3

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;
}
molbdnilo
  • 64,751
  • 3
  • 43
  • 82