0

When providing a function that copies a large object, do I need to provide an explicit override accepting an rvalue to benefit from move assignment?

Most of the discussion of when to explicitly provide rvalue overrides is focused on constructors and assignment operators, or return values. But I can't quite figure out how it applies to copying/move-assigning large objects.

consider:

struct A{
  //my owned data, any large object that implements move assignment
  std::vector<std::string> myvec;

  //pre-c++11, this would assign by copy
  void Set(const std::vector<std::string>& vec){ myvec = vec; }

  //c++11, explicitly move assign
  void Set(std::vector<std::string>&& vec){ myvec = std::move(vec); }
};

A a;
a.Set(std::vector<std::string>({"hello","world"});

If I call Set with an lvalue, the first version of the method (const T& will be called, and the assignment will be done by copy. As it is written, the call at the bottom of the snippet will call the second version of Set (T&&) because the temporary object is an rvalue.

I read somewhere (can't find the reference now) that const T& can bind to rvalues. If that's the case, and my T implements a move-assign operator (as std::vector does), will the first version allow efficient move-assignment of rvalues, making the T&& override unnecessary?

Max Langhof
  • 23,383
  • 5
  • 39
  • 72
thegreatemu
  • 495
  • 2
  • 11
  • 2
    For some applications, I've seen just providing the equivalent of `void Set(std::vector vec){ myvec = std::move(vec); }`. It costs an extra move in all cases but you get away with just one function without having to repeat yourself. – François Andrieux Feb 01 '19 at 17:11
  • 3
    Yes, `const T&` can bind to rvalues. But the `T` object is marked as **`const`**, so can't be moved from. – Pete Becker Feb 01 '19 at 17:11
  • @FrançoisAndrieux Doesn't it cost a copy + move? Copy as the object is passed into the function body, followed by move assignment? I guess for temporaries, the copy is elided, but not for true lvalues...? – thegreatemu Feb 01 '19 at 17:15
  • @thegreatemu I was missing an "extra" in there. It costs and *extra* move. For lvalues you get a copy plus a move, for rvalues you get two moves. – François Andrieux Feb 01 '19 at 17:16
  • Do note that if T in the accepted answer is something that could be constructed by a std::initializer_list, like your vector, this won't allow you to use a list in the call. set_a({1,2,3}) would hae to become set_a(A{1,2,3}) since braced-init-list's don't have a type. You would still need to add a `Set(std::initializer_list)` overload to get the same behavior as you current code. – NathanOliver Feb 01 '19 at 17:23
  • @FrançoisAndrieux It can be pretty costly by requiring allocations where none were required. See my answer here: https://stackoverflow.com/a/46554916/1896169 – Justin Feb 01 '19 at 17:24

0 Answers0