SillyGoose35
SillyGoose35

Reputation: 11

Justifications of forwarding references

Recently, I was reading about forwarding references, and stumbled upon this common example:


void put(const Data& data) { m_data = data; }

void put(Data&& data) {m_data = std::move(data);}

And the solution to this was to introduce a forwarding reference as follows:

template <class T>

void put(T&& data) {m_data = std::forward<T>(data);}  

However, I have noticed there is another solution which seems to be working the same, but slightly cleaner:


void put(Data data) {m_data = std::move(data);}  

This way, if put were to receive some lvalue, only one copy would be called, and if it were to receive an rvalue, only move operations would be called. Which is the same as universal references.

This also uses less templates and results in easier compilation errors.

Given this solution, what are justifications of forwarding references - functionalities that it can provide, that a simple pass-by-value-and-move idiom can't?

Thank you!

Upvotes: 1

Views: 74

Answers (1)

Jerry Coffin
Jerry Coffin

Reputation: 490128

Much of the point of forwarding is to maintain a reference to the original object as long as possible, and only convert to the destination at the last minute, when truly necessary.

What you're suggesting as an alternative is converting to the destination type early, and then depending moving the destination type to keep that from causing problems. Sometimes that might work out. But others it won't. One obvious situation where it won't is when you can assign directly from the source to the destination type, but can't move the destination type at all:

#include <utility>

class Foo {
    char const *m_data;
public:
    Foo &operator=(char const *data) { m_data = data; return *this; }
    Foo() {}
    Foo(Foo const &) = delete;
    Foo(Foo &&) = delete;
};

class Bar {
    Foo m_data;
public:
    template <class T>
    void put(T &&origin) { m_data = std::forward<T>(origin); }

    // void put2(Foo data) { m_data = data; }
};

int main() { 
    Bar b;
    b.put("Some string");
}

Here if we uncomment Bar::put2 (using your technique) the code no longer compiles, because it relies on Foo allowing moving (or copying), which it doesn't.

Upvotes: 3

Related Questions