0

Assume a class that only supports an explicit constructor or move semantics. But not a default constructor or copy constructor.

class Foo
{
private:
    int* _data;
public:
    Foo(int value) : _data(new int(value)) {}

    Foo(Foo&& f) : _data(f._data)
    {
        f._data = nullptr;
    }

    ~Foo() { delete _data;}

    Foo(Foo& f) = delete;
    Foo() = delete;
    Foo& operator=(Foo& f) = delete;
};

Now let's say, there's another component that needs to assume ownership of an instance of the above class. An instance of "Bar" needs to take ownership of a Foo via its constructor.

Foo f(42);
Bar b(std::move(f));     

So far, so good.

But how should the constructor of Bar be declared? I assumed it was this:

class Bar
{
    Foo _f;
public:
    Bar(Foo&& f) : _f(std::move(f)) {}
};

But this also works:

class Bar
{
    Foo _f;
public:
    Bar(Foo f) : _f(std::move(f)) {}
};

What's the difference and which is better? (Or when to choose one over the other?) I didn't even know the latter was possible until I observed Boost's Asio library passing sockets to handlers by value. The socket class has similar constraints as the Foo class above.

selbie
  • 100,020
  • 15
  • 103
  • 173
  • 2
    `Bar(Foo f)` will make a copy of `f`, while `Bar(Foo&& f)` will not. – Ripi2 Dec 22 '19 at 19:12
  • 1
    @Ripi2 - the copy constructor is deleted, so how could a copy of `f` be made? – selbie Dec 22 '19 at 19:13
  • In the first case you have a single move from `Foo(42)` into `_f`, in the second case - two moves, first from `Foo(42)` into `f`, then from `f` into `_f`. – Evg Dec 22 '19 at 19:15
  • `Foo(Foo&& f)` is not deleted. – Ripi2 Dec 22 '19 at 19:17
  • 2
    `Bar(Foo&& f)` is not *"Universal reference"* (or *forwarding reference*). It is simply *rvalue reference*, `template void f(T&&)` would be. – Jarod42 Dec 22 '19 at 19:26

0 Answers0