0

As https://stackoverflow.com/a/3279550/943619 ably describes, the right way to implement operator= is usually:

my_type& operator=(my_type other) {
    swap(*this, other);
    return *this;
}
friend void swap(my_type& lhs, my_type& rhs) { ... }  // This can vary some.

However, Howard Hinnant has pointed out that you can sometimes do significantly better by implementing separate overloads for lvalue and rvalue arguments:

my_type& operator=(const my_type& other) { ... }
my_type& operator=(my_type&& other) { ... }

Clearly the 2-overload solution can save one move in several cases, but such a small improvement is unlikely to show up in benchmarks. I'm looking, instead, for the cases where writing 2 overloads can ~double the performance.

Community
  • 1
  • 1
Jeffrey Yasskin
  • 5,171
  • 2
  • 27
  • 39
  • I suppose when you *have no `swap`* function available it might be simpler to implement the two versions of assignment directly. – Kerrek SB Oct 22 '13 at 16:39
  • "but such a small improvement is unlikely to show up in benchmarks" Have you measured it? ;) What if `sizeof(your_type) >= 100`? What if the move is simple, but cannot be inlined? – dyp Oct 22 '13 at 16:42
  • @KerrekSB: I've added an explicit swap(). Clearer? – Jeffrey Yasskin Oct 22 '13 at 16:45
  • For historical context: the reason I wrote the copy-and-swap Q&A in the first place is that StackOverflow was plagued with beginners making mistakes with the rule of three. I got tired of explaining this and wanted a way to just say "go do this", so I made that Q&A to link to so I could be lazy about it. As noted, the idiom is for a quick and easy and correct implementation of the rule of three, and that was the goal of the Q&A. It is *not* the best nor most efficient way. I only ever use it when I need a quick and correct implementation, but I doubt it should be used in real library code. – GManNickG Oct 22 '13 at 16:45
  • @DyP: Well, I've failed to measure a change in the past, which is what I was saying. :) Even when the type is big, usually it's big by pointing to lots of other objects, whose use swamps the extra move. However, types that contain all their data (std::array, for example) rather than pointing to it are an important exception. 'course, for those, you don't need the rvalue overload. – Jeffrey Yasskin Oct 22 '13 at 16:47
  • 3
    I believe Howard was pretty clear: http://stackoverflow.com/a/9322542/273767 – Cubbi Oct 22 '13 at 16:47
  • 4
    Here's an answer with some measurements: http://stackoverflow.com/a/18303787/576911 – Howard Hinnant Oct 22 '13 at 17:06
  • Basically, with copy-and-`swap` you will always have to copy the direct class members twice, whereas with the direct code you can get away with a single copy, so it might be worth spelling it out when you have lots of members. The added benefit of the copy-swap is that you get exception safety for free, provided you have a non-throwing swap. – Kerrek SB Oct 22 '13 at 17:17
  • possible duplicate of [When is overloading pass by reference (l-value and r-value) preferred to pass-by-value?](http://stackoverflow.com/questions/18303287/when-is-overloading-pass-by-reference-l-value-and-r-value-preferred-to-pass-by) – Jeffrey Yasskin Oct 22 '13 at 18:16

1 Answers1

1

The main problem with the "copy-and-swap" idiom is that it is less efficient.

Even if we have a good swap implementation, we still have 3 instances of the class at once, where the other solution of lvalue and rvalue operators only have 2 instances.

This means we have 50% more memory needed for the copy-and-swap, and it may also have a significant time cost - as you have to acquire resources you otherwise wouldn't need to.

Think of the std::vector case: using a lvalue reference if you do vec1=vec2; and both have the same size - you don't need to do any new and delete at all. In the copy-and-swap you have to create a third vector - so you have an extra new and delete that may take a lot of time (if the vectors are small it is significant) or a lot of memory (if the vectors are big)

The main advantage of the copy-and-swap is exception safety. copy-and-swap gives you Strong exception safety, but at the price of performance.

If you need exception safety - use it. If you need performance (memory, time, any other resources your classes aquire) don't use it.

rabensky
  • 2,864
  • 13
  • 18
  • Note that many C++ libraries, including libc++ and libstdc++, don't allocate anything for a default-constructed vector, making the number of instances less important than the number of non-empty instances, and that's still 2 with the copy-and-swap idiom. – Jeffrey Yasskin Oct 22 '13 at 22:18
  • 2
    @JeffreyYasskin No, it's still 3 with the copy-and-swap if we're talking about the `&` reference (rather than the `&&`) – rabensky Oct 23 '13 at 04:52
  • Ah, yes, you're right. I was neglecting the contents of the instance before the assignment. – Jeffrey Yasskin Oct 23 '13 at 06:35