3

When writing source- and sink-functions, what signatures should I use to benefit from move semantics and why?

T source();
sink(T);

// or

T&& source();
sink(T&&);
R. Martinho Fernandes
  • 228,013
  • 71
  • 433
  • 510
Micha Wiedenmann
  • 19,979
  • 21
  • 92
  • 137

1 Answers1

3

Well, T&& source() is definitely rubbish considering that a source creates and returns a new object. Don't return references to local variables from functions, this rule applies the same for rvalue references as it always applied for lvalue references.

Using sink(T&&) is rather a matter of taste. It doesn't look that clean and streamlined, but on the other hand it underlines the sink-nature of the function more explicitly and makes moving mandatory, even for types that might also be copied. But if T is supposed to be a non-copyable type (like std::unique_ptr) anyway, I'd prefer sink(T), as it just looks a bit more clean and the moving is explicitly stated by the non-copyable nature of the type, anyway.

But nevertheless you will in all cases benefit from move semantics, which is one of their strength, they intergrate well into exisiting code. So T t = source(); will move, as will sink(std::move(t));, no matter which signature sink has. It's just that sink(T) doesn't prevent you from calling it with sink(t); instead of sink(std::move(t)); for copyable types, which would in turn not make use of move semantics. But that is a totally different problem, since using std::move whenever appropriate should be an idiom learned as soon as possible, anyway.

EDIT: It is true, that sink(T) doesn't play well with not efficiently movable types (types using a value-based implementation, i.e. std::array), since it requires a copy where a (rvalue) reference would probably do. And even for movable types not moving at all is still faster than moving (though this should be negligable). But then again, sources and sinks don't play well with value-implemented types in the first place, I think. And in this case T source() wouldn't be that good an idea perfomance-wise, anyway (but neither can it be replaced by T&& source() as described above).

Christian Rau
  • 45,360
  • 10
  • 108
  • 185
  • 1
    Looking at `std::vector` there is `push_back(const T&)` and `push_back(T&&)`. Does your argument also say that `push_back(T)` would be sufficient? – Micha Wiedenmann Dec 21 '12 at 11:03
  • @MichaWiedenmann No, and you're right, that not-moving at all is even faster than moving. Likewise is `push_back(T)` not a good idea for not efficiently movable types (good old strong value types). But I would argue that sources and sinks are rather used for efficiently movabe types, since otherwise `T source()` wouldn't be that good an idea in the first place. – Christian Rau Dec 21 '12 at 11:37