Slide 1

Slide 1 text

Modern C++ Explained : Move Semantics February 5, 2018 by Olve Maudal

Slide 2

Slide 2 text

In this session I will try to explain rvalue references (aka refref) and move semantics. Here is a code snippet to get us started…

Slide 3

Slide 3 text

#include #include void analyze(std::string seq) { std::cout << seq << std::endl; // ... } int main() { std::string seq = "ACTTCTGTATTGGGTCTTTAATAG"; analyze(seq); } In this session I will try to explain rvalue references (aka refref) and move semantics. Here is a code snippet to get us started…

Slide 4

Slide 4 text

#include #include void analyze(std::string seq) { std::cout << seq << std::endl; // ... } int main() { std::string seq = "ACTTCTGTATTGGGTCTTTAATAG"; analyze(seq); } $ c++ tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG $ In this session I will try to explain rvalue references (aka refref) and move semantics. Here is a code snippet to get us started…

Slide 5

Slide 5 text

#include #include void analyze(std::string seq) { std::cout << seq << std::endl; // ... } int main() { std::string seq = "ACTTCTGTATTGGGTCTTTAATAG"; analyze(seq); } $ c++ tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG $ We have a sequence of letters and send it off to a function to be analyzed. In this case, we pass the string by value and the function will be working on a copy of the string. In this session I will try to explain rvalue references (aka refref) and move semantics. Here is a code snippet to get us started…

Slide 6

Slide 6 text

#include #include void analyze(std::string seq) { std::cout << seq << std::endl; // ... } int main() { std::string seq = "ACTTCTGTATTGGGTCTTTAATAG"; analyze(seq); } $ c++ tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG $ We have a sequence of letters and send it off to a function to be analyzed. In this case, we pass the string by value and the function will be working on a copy of the string. In this session I will try to explain rvalue references (aka refref) and move semantics. Here is a code snippet to get us started… For large objects, taking a copy might be inefficient, so passing the object by a reference to a constant (aka const ref) is often recommend.

Slide 7

Slide 7 text

#include #include void analyze(std::string seq) { std::cout << seq << std::endl; // ... } int main() { std::string seq = "ACTTCTGTATTGGGTCTTTAATAG"; analyze(seq); }

Slide 8

Slide 8 text

#include #include void analyze(std::string seq) { std::cout << seq << std::endl; // ... } int main() { std::string seq = "ACTTCTGTATTGGGTCTTTAATAG"; analyze(seq); }

Slide 9

Slide 9 text

#include #include void analyze(const std::string & seq) { std::cout << seq << std::endl; // ... } int main() { std::string seq = "ACTTCTGTATTGGGTCTTTAATAG"; analyze(seq); }

Slide 10

Slide 10 text

#include #include void analyze(const std::string & seq) { std::cout << seq << std::endl; // ... } int main() { std::string seq = "ACTTCTGTATTGGGTCTTTAATAG"; analyze(seq); }

Slide 11

Slide 11 text

#include #include void analyze(const std::string & seq) { std::cout << seq << std::endl; // ... } int main() { std::string seq = "ACTTCTGTATTGGGTCTTTAATAG"; analyze(seq); } And this is probably fine, until the analyze() function needs to modify the sequence. For example…

Slide 12

Slide 12 text

#include #include void analyze(const std::string & seq) { std::cout << seq << std::endl; // ... } int main() { std::string seq = "ACTTCTGTATTGGGTCTTTAATAG"; analyze(seq); }

Slide 13

Slide 13 text

#include #include void analyze(const std::string & seq) { std::cout << seq << std::endl; // ... } int main() { std::string seq = "ACTTCTGTATTGGGTCTTTAATAG"; analyze(seq); }

Slide 14

Slide 14 text

#include #include void analyze(const std::string & seq) { std::cout << seq << std::endl; // ... std::string seq2(seq); if (std::size_t pos = seq2.find("CTG"); pos != seq2.npos) seq2.replace(pos, 3, "..."); std::cout << seq2 << std::endl; // ... } int main() { std::string seq = "ACTTCTGTATTGGGTCTTTAATAG"; analyze(seq); }

Slide 15

Slide 15 text

#include #include void analyze(const std::string & seq) { std::cout << seq << std::endl; // ... std::string seq2(seq); if (std::size_t pos = seq2.find("CTG"); pos != seq2.npos) seq2.replace(pos, 3, "..."); std::cout << seq2 << std::endl; // ... } int main() { std::string seq = "ACTTCTGTATTGGGTCTTTAATAG"; analyze(seq); } $ c++ tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG ACTT...TATTGGGTCTTTAATAG $

Slide 16

Slide 16 text

#include #include void analyze(const std::string & seq) { std::cout << seq << std::endl; // ... std::string seq2(seq); if (std::size_t pos = seq2.find("CTG"); pos != seq2.npos) seq2.replace(pos, 3, "..."); std::cout << seq2 << std::endl; // ... } int main() { std::string seq = "ACTTCTGTATTGGGTCTTTAATAG"; analyze(seq); } Here we take a copy of the sequence and then change the copy. Creating a copy could be expensive for large objects. $ c++ tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG ACTT...TATTGGGTCTTTAATAG $

Slide 17

Slide 17 text

#include #include void analyze(const std::string & seq) { std::cout << seq << std::endl; // ... std::string seq2(seq); if (std::size_t pos = seq2.find("CTG"); pos != seq2.npos) seq2.replace(pos, 3, "..."); std::cout << seq2 << std::endl; // ... } int main() { std::string seq = "ACTTCTGTATTGGGTCTTTAATAG"; analyze(seq); } Here we take a copy of the sequence and then change the copy. Creating a copy could be expensive for large objects. $ c++ tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG ACTT...TATTGGGTCTTTAATAG $ But what if the caller is fine with the idea of letting the analyze() function do whatever it wants with the object we pass to it?

Slide 18

Slide 18 text

#include #include void analyze(const std::string & seq) { std::cout << seq << std::endl; // ... std::string seq2(seq); if (std::size_t pos = seq2.find("CTG"); pos != seq2.npos) seq2.replace(pos, 3, "..."); std::cout << seq2 << std::endl; // ... } int main() { std::string seq = "ACTTCTGTATTGGGTCTTTAATAG"; analyze(seq); } Here we take a copy of the sequence and then change the copy. Creating a copy could be expensive for large objects. $ c++ tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG ACTT...TATTGGGTCTTTAATAG $ Then, in this case, taking the argument as a non-const ref might be a better solution. But what if the caller is fine with the idea of letting the analyze() function do whatever it wants with the object we pass to it?

Slide 19

Slide 19 text

#include #include void analyze(const std::string & seq) { std::cout << seq << std::endl; // ... std::string seq2(seq); if (std::size_t pos = seq2.find("CTG"); pos != seq2.npos) seq2.replace(pos, 3, "..."); std::cout << seq2 << std::endl; // ... } int main() { std::string seq = "ACTTCTGTATTGGGTCTTTAATAG"; analyze(seq); }

Slide 20

Slide 20 text

#include #include void analyze(const std::string & seq) { std::cout << seq << std::endl; // ... std::string seq2(seq); if (std::size_t pos = seq2.find("CTG"); pos != seq2.npos) seq2.replace(pos, 3, "..."); std::cout << seq2 << std::endl; // ... } int main() { std::string seq = "ACTTCTGTATTGGGTCTTTAATAG"; analyze(seq); }

Slide 21

Slide 21 text

#include #include void analyze(std::string & seq) { std::cout << seq << std::endl; // ... std::string seq2(seq); if (std::size_t pos = seq2.find("CTG"); pos != seq2.npos) seq2.replace(pos, 3, "..."); std::cout << seq2 << std::endl; // ... } int main() { std::string seq = "ACTTCTGTATTGGGTCTTTAATAG"; analyze(seq); }

Slide 22

Slide 22 text

#include #include void analyze(std::string & seq) { std::cout << seq << std::endl; // ... std::string seq2(seq); if (std::size_t pos = seq2.find("CTG"); pos != seq2.npos) seq2.replace(pos, 3, "..."); std::cout << seq2 << std::endl; // ... } int main() { std::string seq = "ACTTCTGTATTGGGTCTTTAATAG"; analyze(seq); }

Slide 23

Slide 23 text

#include #include void analyze(std::string & seq) { std::cout << seq << std::endl; // ... std::string seq2(seq); if (std::size_t pos = seq2.find("CTG"); pos != seq2.npos) seq2.replace(pos, 3, "..."); std::cout << seq2 << std::endl; // ... } int main() { std::string seq = "ACTTCTGTATTGGGTCTTTAATAG"; analyze(seq); }

Slide 24

Slide 24 text

#include #include void analyze(std::string & seq) { std::cout << seq << std::endl; // ... std::string seq2(seq); if (std::size_t pos = seq2.find("CTG"); pos != seq2.npos) seq2.replace(pos, 3, "..."); std::cout << seq2 << std::endl; // ... } int main() { std::string seq = "ACTTCTGTATTGGGTCTTTAATAG"; analyze(seq); }

Slide 25

Slide 25 text

#include #include void analyze(std::string & seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } int main() { std::string seq = "ACTTCTGTATTGGGTCTTTAATAG"; analyze(seq); }

Slide 26

Slide 26 text

#include #include void analyze(std::string & seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } int main() { std::string seq = "ACTTCTGTATTGGGTCTTTAATAG"; analyze(seq); }

Slide 27

Slide 27 text

#include #include void analyze(std::string & seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } int main() { std::string seq = "ACTTCTGTATTGGGTCTTTAATAG"; analyze(seq); }

Slide 28

Slide 28 text

#include #include void analyze(std::string & seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } int main() { std::string seq = "ACTTCTGTATTGGGTCTTTAATAG"; analyze(seq); } Of course, we should be sceptical about functions that modifies its arguments like this - but in this case, for very big data structures, it might be exactly the design solution for the problem we are trying to solve.

Slide 29

Slide 29 text

#include #include void analyze(std::string & seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } int main() { std::string seq = "ACTTCTGTATTGGGTCTTTAATAG"; analyze(seq); } Of course, we should be sceptical about functions that modifies its arguments like this - but in this case, for very big data structures, it might be exactly the design solution for the problem we are trying to solve. However…

Slide 30

Slide 30 text

#include #include void analyze(std::string & seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } int main() { std::string seq = "ACTTCTGTATTGGGTCTTTAATAG"; analyze(seq); }

Slide 31

Slide 31 text

#include #include void analyze(std::string & seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } int main() { std::string seq = "ACTTCTGTATTGGGTCTTTAATAG"; analyze(seq); }

Slide 32

Slide 32 text

#include #include void analyze(std::string & seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } int main() { std::string seq = "ACTTCTGTATTGGGTCTTTAATAG"; analyze(seq); }

Slide 33

Slide 33 text

#include #include void analyze(std::string & seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { std::string seq = extract(); analyze(seq); }

Slide 34

Slide 34 text

#include #include void analyze(std::string & seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { std::string seq = extract(); analyze(seq); }

Slide 35

Slide 35 text

#include #include void analyze(std::string & seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { std::string seq = extract(); analyze(seq); } $ c++ tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG ACTT...TATTGGGTCTTTAATAG $

Slide 36

Slide 36 text

#include #include void analyze(std::string & seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { std::string seq = extract(); analyze(seq); }

Slide 37

Slide 37 text

#include #include void analyze(std::string & seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { std::string seq = extract(); analyze(seq); }

Slide 38

Slide 38 text

#include #include void analyze(std::string & seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { analyze(extract()); }

Slide 39

Slide 39 text

#include #include void analyze(std::string & seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { analyze(extract()); }

Slide 40

Slide 40 text

#include #include void analyze(std::string & seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { analyze(extract()); } Now we get an error.

Slide 41

Slide 41 text

#include #include void analyze(std::string & seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { analyze(extract()); } $ c++ tour.cpp && ./a.out error: no matching function for call to 'analyze' candidate function expects an l-value $ Now we get an error.

Slide 42

Slide 42 text

#include #include void analyze(std::string & seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { analyze(extract()); } $ c++ tour.cpp && ./a.out error: no matching function for call to 'analyze' candidate function expects an l-value $ Now we get an error. Because we try to pass an unnamed value to a function that needs to “borrow” the object.

Slide 43

Slide 43 text

#include #include void analyze(std::string & seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { analyze(extract()); } $ c++ tour.cpp && ./a.out error: no matching function for call to 'analyze' candidate function expects an l-value $ Now we get an error. Because we try to pass an unnamed value to a function that needs to “borrow” the object. A slightly better (but still imprecise*) way of saying this is that the analyze() function expects an lvalue (something that can appear on the left hand side of an assignment), while we are trying to pass it an rvalue (something that needs to be on the right hand side of an assignment statement). (*) the legalese for this includes discussions about five, partly overlapping value categories: glvalues, prvalues, xvalues, lvalues and rvalues... a nice topic for later presentations

Slide 44

Slide 44 text

#include #include void analyze(std::string & seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { analyze(extract()); } $ c++ tour.cpp && ./a.out error: no matching function for call to 'analyze' candidate function expects an l-value $ Now we get an error. Because we try to pass an unnamed value to a function that needs to “borrow” the object. A slightly better (but still imprecise*) way of saying this is that the analyze() function expects an lvalue (something that can appear on the left hand side of an assignment), while we are trying to pass it an rvalue (something that needs to be on the right hand side of an assignment statement). (*) the legalese for this includes discussions about five, partly overlapping value categories: glvalues, prvalues, xvalues, lvalues and rvalues... a nice topic for later presentations Is there a way to let the analyze() function take an rvalue?

Slide 45

Slide 45 text

#include #include void analyze(std::string & seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { analyze(extract()); } $ c++ tour.cpp && ./a.out error: no matching function for call to 'analyze' candidate function expects an l-value $ Now we get an error. Because we try to pass an unnamed value to a function that needs to “borrow” the object. A slightly better (but still imprecise*) way of saying this is that the analyze() function expects an lvalue (something that can appear on the left hand side of an assignment), while we are trying to pass it an rvalue (something that needs to be on the right hand side of an assignment statement). (*) the legalese for this includes discussions about five, partly overlapping value categories: glvalues, prvalues, xvalues, lvalues and rvalues... a nice topic for later presentations Is there a way to let the analyze() function take an rvalue? Yes! And that feature was introduced in C++11

Slide 46

Slide 46 text

#include #include void analyze(std::string & seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { analyze(extract()); }

Slide 47

Slide 47 text

#include #include void analyze(std::string & seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { analyze(extract()); }

Slide 48

Slide 48 text

#include #include void analyze(std::string & seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { analyze(extract()); }

Slide 49

Slide 49 text

#include #include void analyze(std::string && seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { analyze(extract()); }

Slide 50

Slide 50 text

#include #include void analyze(std::string && seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { analyze(extract()); } $ c++ tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG ACTT...TATTGGGTCTTTAATAG $

Slide 51

Slide 51 text

#include #include void analyze(std::string && seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { analyze(extract()); } $ c++ tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG ACTT...TATTGGGTCTTTAATAG $ So now we have an analyze() function that can take reference to a so called rvalue.

Slide 52

Slide 52 text

#include #include void analyze(std::string && seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { analyze(extract()); } $ c++ tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG ACTT...TATTGGGTCTTTAATAG $ So now we have an analyze() function that can take reference to a so called rvalue. However, can this same function also take lvalues?

Slide 53

Slide 53 text

#include #include void analyze(std::string && seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { analyze(extract()); } $ c++ tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG ACTT...TATTGGGTCTTTAATAG $ So now we have an analyze() function that can take reference to a so called rvalue. The answer is NO. However, can this same function also take lvalues?

Slide 54

Slide 54 text

#include #include void analyze(std::string && seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { analyze(extract()); } $ c++ tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG ACTT...TATTGGGTCTTTAATAG $ So now we have an analyze() function that can take reference to a so called rvalue. The answer is NO. Let’s demonstrate… However, can this same function also take lvalues?

Slide 55

Slide 55 text

#include #include void analyze(std::string && seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { analyze(extract()); }

Slide 56

Slide 56 text

#include #include void analyze(std::string && seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { analyze(extract()); }

Slide 57

Slide 57 text

#include #include void analyze(std::string && seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { std::string seq = extract(); analyze(seq); }

Slide 58

Slide 58 text

#include #include void analyze(std::string && seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { std::string seq = extract(); analyze(seq); } $ c++ tour.cpp && ./a.out error: no matching function for call to ‘analyze’ no known conversion from ‘std::string' to 'std::string &&’ $

Slide 59

Slide 59 text

#include #include void analyze(std::string && seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { std::string seq = extract(); analyze(seq); } $ c++ tour.cpp && ./a.out error: no matching function for call to ‘analyze’ no known conversion from ‘std::string' to 'std::string &&’ $ Now we get an error because the function expected an rvalue but we try to call it with an lvalue.

Slide 60

Slide 60 text

#include #include void analyze(std::string && seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { std::string seq = extract(); analyze(seq); }

Slide 61

Slide 61 text

#include #include void analyze(std::string && seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { std::string seq = extract(); analyze(seq); } It is possible to cast an lvalue into a rvalue by using std::move()

Slide 62

Slide 62 text

#include #include void analyze(std::string && seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { std::string seq = extract(); analyze(seq); } It is possible to cast an lvalue into a rvalue by using std::move()

Slide 63

Slide 63 text

#include #include void analyze(std::string && seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { std::string seq = extract(); analyze(seq); } It is possible to cast an lvalue into a rvalue by using std::move()

Slide 64

Slide 64 text

#include #include void analyze(std::string && seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { std::string seq = extract(); analyze(std::move(seq)); }

Slide 65

Slide 65 text

#include #include void analyze(std::string && seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { std::string seq = extract(); analyze(std::move(seq)); } $ c++ tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG ACTT...TATTGGGTCTTTAATAG $

Slide 66

Slide 66 text

#include #include void analyze(std::string && seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { std::string seq = extract(); analyze(std::move(seq)); } $ c++ tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG ACTT...TATTGGGTCTTTAATAG $ The use of std::move here expresses the idea that “you can take the object. It’s yours. I will clean up the mess, but I promise not to assume anything about the object after you are done.”

Slide 67

Slide 67 text

#include #include void analyze(std::string && seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { std::string seq = extract(); analyze(std::move(seq)); } $ c++ tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG ACTT...TATTGGGTCTTTAATAG $ The use of std::move here expresses the idea that “you can take the object. It’s yours. I will clean up the mess, but I promise not to assume anything about the object after you are done.” Having said that… it is important to understand that there is nothing magical with std::move, it is just a simple cast that generates no code, but tells the compiler that it is ok to deal with the object (or more generally, the expression) as if it was an rvalue. (At some point I guess it was considered to call it std::rvalue_cast instead)

Slide 68

Slide 68 text

#include #include void analyze(std::string && seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { std::string seq = extract(); analyze(std::move(seq)); } $ c++ tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG ACTT...TATTGGGTCTTTAATAG $ The use of std::move here expresses the idea that “you can take the object. It’s yours. I will clean up the mess, but I promise not to assume anything about the object after you are done.” When moving an object like this, the only thing you can do afterwards is to delete it or give it a new state or invoke member functions that do not have any assumption of its internal state. If you don’t know exactly what you are doing, it is probably best to not touch the object at all, just make sure that the object eventually gets destroyed. Having said that… it is important to understand that there is nothing magical with std::move, it is just a simple cast that generates no code, but tells the compiler that it is ok to deal with the object (or more generally, the expression) as if it was an rvalue. (At some point I guess it was considered to call it std::rvalue_cast instead)

Slide 69

Slide 69 text

#include #include void analyze(std::string && seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { std::string seq = extract(); analyze(std::move(seq)); }

Slide 70

Slide 70 text

#include #include void analyze(std::string && seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { std::string seq = extract(); analyze(std::move(seq)); }

Slide 71

Slide 71 text

#include #include void analyze(std::string && seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { std::string seq = extract(); analyze(std::move(seq)); }

Slide 72

Slide 72 text

#include #include void analyze(std::string && seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { std::string seq = extract(); analyze(std::move(seq)); }

Slide 73

Slide 73 text

#include #include void analyze(std::string && seq) { std::cout << seq << std::endl; // ... if (std::size_t pos = seq.find("CTG"); pos != seq.npos) seq.replace(pos, 3, "..."); std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { std::string seq = extract(); analyze(std::move(seq)); }

Slide 74

Slide 74 text

#include #include void analyze(std::string && seq) { std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { std::string seq = extract(); analyze(std::move(seq)); }

Slide 75

Slide 75 text

#include #include void analyze(std::string && seq) { std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { std::string seq = extract(); analyze(std::move(seq)); } We have seen rvalue reference and move semantics from a users point of view. I will now show how to implement a class that supports move semantics.

Slide 76

Slide 76 text

#include #include void analyze(std::string && seq) { std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { std::string seq = extract(); analyze(std::move(seq)); } We have seen rvalue reference and move semantics from a users point of view. I will now show how to implement a class that supports move semantics. Let’s replace std::string with a class Contig that can hold a sequence of these letters (representing the nucleobases adenine, guanine, thymine and cytosine).

Slide 77

Slide 77 text

#include #include void analyze(std::string && seq) { std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { std::string seq = extract(); analyze(std::move(seq)); }

Slide 78

Slide 78 text

#include #include void analyze(std::string && seq) { std::cout << seq << std::endl; // ... } std::string extract() { // ... return "ACTTCTGTATTGGGTCTTTAATAG"; } int main() { std::string seq = extract(); analyze(std::move(seq)); }

Slide 79

Slide 79 text

#include #include #include #include class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... } Contig extract() { // ... return Contig("ACTTCTGTATTGGGTCTTTAATAG"); } int main() { Contig seq = extract(); analyze(std::move(seq)); }

Slide 80

Slide 80 text

#include #include #include #include class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... } Contig extract() { // ... return Contig("ACTTCTGTATTGGGTCTTTAATAG"); } int main() { Contig seq = extract(); analyze(std::move(seq)); }

Slide 81

Slide 81 text

#include #include #include #include class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... } Contig extract() { // ... return Contig("ACTTCTGTATTGGGTCTTTAATAG"); } int main() { Contig seq = extract(); analyze(std::move(seq)); }

Slide 82

Slide 82 text

#include #include #include #include class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... } Contig extract() { // ... return Contig("ACTTCTGTATTGGGTCTTTAATAG"); } int main() { Contig seq = extract(); analyze(std::move(seq)); }

Slide 83

Slide 83 text

#include #include #include #include class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... } Contig extract() { // ... return Contig("ACTTCTGTATTGGGTCTTTAATAG"); } int main() { Contig seq = extract(); analyze(std::move(seq)); } $ c++ tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG $

Slide 84

Slide 84 text

#include #include #include #include class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... } Contig extract() { // ... return Contig("ACTTCTGTATTGGGTCTTTAATAG"); } int main() { Contig seq = extract(); analyze(std::move(seq)); } $ c++ tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG $ Looks good, but we are not done yet. There is some essential stuff missing.

Slide 85

Slide 85 text

#include #include #include #include class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... } Contig extract() { // ... return Contig("ACTTCTGTATTGGGTCTTTAATAG"); } int main() { Contig seq = extract(); analyze(std::move(seq)); } $ c++ tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG $ Looks good, but we are not done yet. There is some essential stuff missing.

Slide 86

Slide 86 text

#include #include #include #include class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... } Contig extract() { // ... return Contig("ACTTCTGTATTGGGTCTTTAATAG"); } int main() { Contig seq = extract(); analyze(std::move(seq)); } $ c++ tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG $ Looks good, but we are not done yet. There is some essential stuff missing. First, have a look at what happens if we forget to write our own destructor for this class.

Slide 87

Slide 87 text

#include #include #include #include class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } // ~Contig() {} private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... } Contig extract() { // ... return Contig("ACTTCTGTATTGGGTCTTTAATAG"); } int main() { Contig seq = extract(); analyze(std::move(seq)); }

Slide 88

Slide 88 text

#include #include #include #include class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } // ~Contig() {} private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... } Contig extract() { // ... return Contig("ACTTCTGTATTGGGTCTTTAATAG"); } int main() { Contig seq = extract(); analyze(std::move(seq)); } $ c++ -fsanitize=leak,address tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG detected memory leaks $

Slide 89

Slide 89 text

#include #include #include #include class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } // ~Contig() {} private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... } Contig extract() { // ... return Contig("ACTTCTGTATTGGGTCTTTAATAG"); } int main() { Contig seq = extract(); analyze(std::move(seq)); } $ c++ -fsanitize=leak,address tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG detected memory leaks $ If we do not implement a user defined destructor for this class, an empty implicit destructor will be created for us and using the class might result in memory leakage.

Slide 90

Slide 90 text

#include #include #include #include class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } // ~Contig() {} private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... } Contig extract() { // ... return Contig("ACTTCTGTATTGGGTCTTTAATAG"); } int main() { Contig seq = extract(); analyze(std::move(seq)); } $ c++ -fsanitize=leak,address tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG detected memory leaks $ If we do not implement a user defined destructor for this class, an empty implicit destructor will be created for us and using the class might result in memory leakage. However, the destructor is not the only special member that will be implicitly created if we don’t specify them explicitly. There are four more special members we need to consider.

Slide 91

Slide 91 text

#include #include #include #include class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } // ~Contig() {} private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... } Contig extract() { // ... return Contig("ACTTCTGTATTGGGTCTTTAATAG"); } int main() { Contig seq = extract(); analyze(std::move(seq)); }

Slide 92

Slide 92 text

#include #include #include #include class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } // ~Contig() {} // Contig(const Contig & other) : size(other.size), data(other.data) {} // Contig & operator=(const Contig & other) { size = other.size; data = other.data; return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... } Contig extract() { // ... return Contig("ACTTCTGTATTGGGTCTTTAATAG"); } int main() { Contig seq = extract(); analyze(std::move(seq)); }

Slide 93

Slide 93 text

#include #include #include #include class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } // ~Contig() {} // Contig(const Contig & other) : size(other.size), data(other.data) {} // Contig & operator=(const Contig & other) { size = other.size; data = other.data; return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... } Contig extract() { // ... return Contig("ACTTCTGTATTGGGTCTTTAATAG"); } int main() { Contig seq = extract(); analyze(std::move(seq)); } By default, this is approximately what we get for our class. They are all doing the “wrong” thing

Slide 94

Slide 94 text

#include #include #include #include class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } // ~Contig() {} // Contig(const Contig & other) : size(other.size), data(other.data) {} // Contig & operator=(const Contig & other) { size = other.size; data = other.data; return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... } Contig extract() { // ... return Contig("ACTTCTGTATTGGGTCTTTAATAG"); } int main() { Contig seq = extract(); analyze(std::move(seq)); } By default, this is approximately what we get for our class. They are all doing the “wrong” thing For classes that manages resources, you need to be aware of all the special member functions that the compiler might automatically generate for you if you don’t specify them explicitly: - destructor - copy constructor - copy assignment - move constructor - move assignment

Slide 95

Slide 95 text

#include #include #include #include class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } // ~Contig() {} // Contig(const Contig & other) : size(other.size), data(other.data) {} // Contig & operator=(const Contig & other) { size = other.size; data = other.data; return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... } Contig extract() { // ... return Contig("ACTTCTGTATTGGGTCTTTAATAG"); } int main() { Contig seq = extract(); analyze(std::move(seq)); }

Slide 96

Slide 96 text

#include #include #include #include class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } // ~Contig() {} // Contig(const Contig & other) : size(other.size), data(other.data) {} // Contig & operator=(const Contig & other) { size = other.size; data = other.data; return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... } Contig extract() { // ... return Contig("ACTTCTGTATTGGGTCTTTAATAG"); } int main() { Contig seq = extract(); analyze(std::move(seq)); } Let's zoom in on the class and the analyze() function. Then implement the special member functions one by one after first illustrating the problem and then providing a fix.

Slide 97

Slide 97 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } // ~Contig() {} // Contig(const Contig & other) : size(other.size), data(other.data) {} // Contig & operator=(const Contig & other) { size = other.size; data = other.data; return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... }

Slide 98

Slide 98 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } // ~Contig() {} // Contig(const Contig & other) : size(other.size), data(other.data) {} // Contig & operator=(const Contig & other) { size = other.size; data = other.data; return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... }

Slide 99

Slide 99 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } // ~Contig() {} // Contig(const Contig & other) : size(other.size), data(other.data) {} // Contig & operator=(const Contig & other) { size = other.size; data = other.data; return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... }

Slide 100

Slide 100 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } // ~Contig() {} // Contig(const Contig & other) : size(other.size), data(other.data) {} // Contig & operator=(const Contig & other) { size = other.size; data = other.data; return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... }

Slide 101

Slide 101 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } // ~Contig() {} // Contig(const Contig & other) : size(other.size), data(other.data) {} // Contig & operator=(const Contig & other) { size = other.size; data = other.data; return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... }

Slide 102

Slide 102 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } // ~Contig() {} // Contig(const Contig & other) : size(other.size), data(other.data) {} // Contig & operator=(const Contig & other) { size = other.size; data = other.data; return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... }

Slide 103

Slide 103 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } // ~Contig() {} // Contig(const Contig & other) : size(other.size), data(other.data) {} // Contig & operator=(const Contig & other) { size = other.size; data = other.data; return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... }

Slide 104

Slide 104 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } // ~Contig() {} // Contig(const Contig & other) : size(other.size), data(other.data) {} // Contig & operator=(const Contig & other) { size = other.size; data = other.data; return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... } $ c++ -fsanitize=leak,address tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG detected memory leaks $

Slide 105

Slide 105 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } // ~Contig() {} // Contig(const Contig & other) : size(other.size), data(other.data) {} // Contig & operator=(const Contig & other) { size = other.size; data = other.data; return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... } $ c++ -fsanitize=leak,address tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG detected memory leaks $ The first one is obvious. If you grab a resource in the constructor, it is usually a good idea to release it in the destructor.

Slide 106

Slide 106 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } // ~Contig() {} // Contig(const Contig & other) : size(other.size), data(other.data) {} // Contig & operator=(const Contig & other) { size = other.size; data = other.data; return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... } $ c++ -fsanitize=leak,address tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG detected memory leaks $ The first one is obvious. If you grab a resource in the constructor, it is usually a good idea to release it in the destructor.

Slide 107

Slide 107 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } // Contig(const Contig & other) : size(other.size), data(other.data) {} // Contig & operator=(const Contig & other) { size = other.size; data = other.data; return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... }

Slide 108

Slide 108 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } // Contig(const Contig & other) : size(other.size), data(other.data) {} // Contig & operator=(const Contig & other) { size = other.size; data = other.data; return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... } $ c++ -fsanitize=leak,address tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG $

Slide 109

Slide 109 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } // Contig(const Contig & other) : size(other.size), data(other.data) {} // Contig & operator=(const Contig & other) { size = other.size; data = other.data; return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... }

Slide 110

Slide 110 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } // Contig(const Contig & other) : size(other.size), data(other.data) {} // Contig & operator=(const Contig & other) { size = other.size; data = other.data; return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... } The problem with the implicit copy constructor can be illustrated by creating a temporary copy in the analyze() function. Eg…

Slide 111

Slide 111 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } // Contig(const Contig & other) : size(other.size), data(other.data) {} // Contig & operator=(const Contig & other) { size = other.size; data = other.data; return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... } The problem with the implicit copy constructor can be illustrated by creating a temporary copy in the analyze() function. Eg…

Slide 112

Slide 112 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } // Contig(const Contig & other) : size(other.size), data(other.data) {} // Contig & operator=(const Contig & other) { size = other.size; data = other.data; return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... } The problem with the implicit copy constructor can be illustrated by creating a temporary copy in the analyze() function. Eg…

Slide 113

Slide 113 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } // Contig(const Contig & other) : size(other.size), data(other.data) {} // Contig & operator=(const Contig & other) { size = other.size; data = other.data; return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(seq); // ... }

Slide 114

Slide 114 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } // Contig(const Contig & other) : size(other.size), data(other.data) {} // Contig & operator=(const Contig & other) { size = other.size; data = other.data; return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(seq); // ... } $ c++ -fsanitize=leak,address tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG attempting double-free $

Slide 115

Slide 115 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } // Contig(const Contig & other) : size(other.size), data(other.data) {} // Contig & operator=(const Contig & other) { size = other.size; data = other.data; return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(seq); // ... } $ c++ -fsanitize=leak,address tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG attempting double-free $ The implicit default copy constructor makes a shallow copy of the object so we end up with two pointers to the same memory object. When tmp and seq are destroyed both of them will try to free the same allocated resource.

Slide 116

Slide 116 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } // Contig(const Contig & other) : size(other.size), data(other.data) {} // Contig & operator=(const Contig & other) { size = other.size; data = other.data; return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(seq); // ... } $ c++ -fsanitize=leak,address tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG attempting double-free $ The implicit default copy constructor makes a shallow copy of the object so we end up with two pointers to the same memory object. When tmp and seq are destroyed both of them will try to free the same allocated resource. We fix this by writing code so that the copy constructor allocates a new memory array and copies data into it.

Slide 117

Slide 117 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } // Contig(const Contig & other) : size(other.size), data(other.data) {} // Contig & operator=(const Contig & other) { size = other.size; data = other.data; return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(seq); // ... }

Slide 118

Slide 118 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } // Contig(const Contig & other) : size(other.size), data(other.data) {} // Contig & operator=(const Contig & other) { size = other.size; data = other.data; return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(seq); // ... }

Slide 119

Slide 119 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } // Contig & operator=(const Contig & other) { size = other.size; data = other.data; return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(seq); // ... }

Slide 120

Slide 120 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } // Contig & operator=(const Contig & other) { size = other.size; data = other.data; return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(seq); // ... } $ c++ -fsanitize=leak,address tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG $

Slide 121

Slide 121 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } // Contig & operator=(const Contig & other) { size = other.size; data = other.data; return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(seq); // ... }

Slide 122

Slide 122 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } // Contig & operator=(const Contig & other) { size = other.size; data = other.data; return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(seq); // ... } The next problem can be illustrated by assigning a Contig object to another Contig object. Eg...

Slide 123

Slide 123 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } // Contig & operator=(const Contig & other) { size = other.size; data = other.data; return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(seq); // ... } The next problem can be illustrated by assigning a Contig object to another Contig object. Eg...

Slide 124

Slide 124 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } // Contig & operator=(const Contig & other) { size = other.size; data = other.data; return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(seq); // ... } The next problem can be illustrated by assigning a Contig object to another Contig object. Eg...

Slide 125

Slide 125 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } // Contig & operator=(const Contig & other) { size = other.size; data = other.data; return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(seq); seq = tmp; // ... }

Slide 126

Slide 126 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } // Contig & operator=(const Contig & other) { size = other.size; data = other.data; return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(seq); seq = tmp; // ... } $ c++ -fsanitize=leak,address tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG attempting double-free $

Slide 127

Slide 127 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } // Contig & operator=(const Contig & other) { size = other.size; data = other.data; return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(seq); seq = tmp; // ... } $ c++ -fsanitize=leak,address tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG attempting double-free $ It is basically the same problem as before. We get a shallow copy by default. We need to write the code to reallocate our array and then copy the elements from the other object. Here we use a so-called copy-swap idiom to implement the copy assignment operator.

Slide 128

Slide 128 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } // Contig & operator=(const Contig & other) { size = other.size; data = other.data; return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(seq); seq = tmp; // ... } $ c++ -fsanitize=leak,address tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG attempting double-free $ It is basically the same problem as before. We get a shallow copy by default. We need to write the code to reallocate our array and then copy the elements from the other object. Here we use a so-called copy-swap idiom to implement the copy assignment operator.

Slide 129

Slide 129 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(seq); seq = tmp; // ... }

Slide 130

Slide 130 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(seq); seq = tmp; // ... } $ c++ -fsanitize=leak,address tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG $

Slide 131

Slide 131 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(seq); seq = tmp; // ... }

Slide 132

Slide 132 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(seq); seq = tmp; // ... } Now, let's try to invoke the implicit move constructor.

Slide 133

Slide 133 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(seq); seq = tmp; // ... } Now, let's try to invoke the implicit move constructor.

Slide 134

Slide 134 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(seq); seq = tmp; // ... } Now, let's try to invoke the implicit move constructor.

Slide 135

Slide 135 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(std::move(seq)); // ... }

Slide 136

Slide 136 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(std::move(seq)); // ... } $ c++ -fsanitize=leak,address tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG $

Slide 137

Slide 137 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(std::move(seq)); // ... } $ c++ -fsanitize=leak,address tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG $ Huh? No error?

Slide 138

Slide 138 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(std::move(seq)); // ... } When there is a user-declared destructor and/or a user- declared copy constructor, then no default move constructor will be provided, and the fallback is to invoke the copy constructor. $ c++ -fsanitize=leak,address tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG $ Huh? No error?

Slide 139

Slide 139 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(std::move(seq)); // ... } When there is a user-declared destructor and/or a user- declared copy constructor, then no default move constructor will be provided, and the fallback is to invoke the copy constructor. $ c++ -fsanitize=leak,address tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG $ We can ask for a (faulty) default move constructor to be generated like this... Huh? No error?

Slide 140

Slide 140 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(std::move(seq)); // ... } When there is a user-declared destructor and/or a user- declared copy constructor, then no default move constructor will be provided, and the fallback is to invoke the copy constructor. $ c++ -fsanitize=leak,address tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG $ We can ask for a (faulty) default move constructor to be generated like this... Huh? No error?

Slide 141

Slide 141 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} Contig(Contig && other) = default; // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(std::move(seq)); // ... }

Slide 142

Slide 142 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} Contig(Contig && other) = default; // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(std::move(seq)); // ... } $ c++ -fsanitize=leak,address tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG attempting double-free $

Slide 143

Slide 143 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} Contig(Contig && other) = default; // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(std::move(seq)); // ... } $ c++ -fsanitize=leak,address tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG attempting double-free $ And now it fails, as expected, since the default move constructor for this class is also just doing a shallow copy of the data members. Remember that std::move is not a magical function, think about it as a cast that is not generating any code.

Slide 144

Slide 144 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} Contig(Contig && other) = default; // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(std::move(seq)); // ... } $ c++ -fsanitize=leak,address tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG attempting double-free $ And now it fails, as expected, since the default move constructor for this class is also just doing a shallow copy of the data members. Remember that std::move is not a magical function, think about it as a cast that is not generating any code. You can also ask for the move constructor to be deleted. In this case we get a compilation error because we still have a user-declared move constructor (even if it is deleted), and therefor there will be no fallback to the copy ctor.

Slide 145

Slide 145 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} Contig(Contig && other) = default; // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(std::move(seq)); // ... } $ c++ -fsanitize=leak,address tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG attempting double-free $ And now it fails, as expected, since the default move constructor for this class is also just doing a shallow copy of the data members. Remember that std::move is not a magical function, think about it as a cast that is not generating any code. You can also ask for the move constructor to be deleted. In this case we get a compilation error because we still have a user-declared move constructor (even if it is deleted), and therefor there will be no fallback to the copy ctor.

Slide 146

Slide 146 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} Contig(Contig && other) = default; // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(std::move(seq)); // ... } $ c++ -fsanitize=leak,address tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG attempting double-free $ And now it fails, as expected, since the default move constructor for this class is also just doing a shallow copy of the data members. Remember that std::move is not a magical function, think about it as a cast that is not generating any code. You can also ask for the move constructor to be deleted. In this case we get a compilation error because we still have a user-declared move constructor (even if it is deleted), and therefor there will be no fallback to the copy ctor.

Slide 147

Slide 147 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} Contig(Contig && other) = delete; // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(std::move(seq)); // ... }

Slide 148

Slide 148 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} Contig(Contig && other) = delete; // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(std::move(seq)); // ... } $ c++ -fsanitize=leak,address tour.cpp && ./a.out call to deleted constructor of 'Contig' $

Slide 149

Slide 149 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} Contig(Contig && other) = delete; // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(std::move(seq)); // ... } $ c++ -fsanitize=leak,address tour.cpp && ./a.out call to deleted constructor of 'Contig' $ or we can implement it properly

Slide 150

Slide 150 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } // Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) {} Contig(Contig && other) = delete; // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(std::move(seq)); // ... } $ c++ -fsanitize=leak,address tour.cpp && ./a.out call to deleted constructor of 'Contig' $ or we can implement it properly

Slide 151

Slide 151 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } Contig(Contig && other) : size(0), data(nullptr) { std::swap(size, other.size); std::swap(data, other.data); } // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(std::move(seq)); // ... }

Slide 152

Slide 152 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } Contig(Contig && other) : size(0), data(nullptr) { std::swap(size, other.size); std::swap(data, other.data); } // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(std::move(seq)); // ... } $ c++ -fsanitize=leak,address tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG $

Slide 153

Slide 153 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } Contig(Contig && other) : size(0), data(nullptr) { std::swap(size, other.size); std::swap(data, other.data); } // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(std::move(seq)); // ... } $ c++ -fsanitize=leak,address tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG $ finally...

Slide 154

Slide 154 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } Contig(Contig && other) : size(0), data(nullptr) { std::swap(size, other.size); std::swap(data, other.data); } // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(std::move(seq)); // ... } $ c++ -fsanitize=leak,address tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG $ finally...

Slide 155

Slide 155 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } Contig(Contig && other) : size(0), data(nullptr) { std::swap(size, other.size); std::swap(data, other.data); } // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(""); tmp = std::move(seq); // ... }

Slide 156

Slide 156 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } Contig(Contig && other) : size(0), data(nullptr) { std::swap(size, other.size); std::swap(data, other.data); } // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(""); tmp = std::move(seq); // ... } $ c++ -fsanitize=leak,address tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG $

Slide 157

Slide 157 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } Contig(Contig && other) : size(0), data(nullptr) { std::swap(size, other.size); std::swap(data, other.data); } // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(""); tmp = std::move(seq); // ... } $ c++ -fsanitize=leak,address tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG $ It is the copy assignment that is called now.

Slide 158

Slide 158 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } Contig(Contig && other) : size(0), data(nullptr) { std::swap(size, other.size); std::swap(data, other.data); } // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(""); tmp = std::move(seq); // ... } $ c++ -fsanitize=leak,address tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG $ It is the copy assignment that is called now.

Slide 159

Slide 159 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } Contig(Contig && other) : size(0), data(nullptr) { std::swap(size, other.size); std::swap(data, other.data); } // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } Contig & operator=(Contig && other) = default; private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(""); tmp = std::move(seq); // ... }

Slide 160

Slide 160 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } Contig(Contig && other) : size(0), data(nullptr) { std::swap(size, other.size); std::swap(data, other.data); } // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } Contig & operator=(Contig && other) = default; private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(""); tmp = std::move(seq); // ... } $ c++ -fsanitize=leak,address tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG attempting double-free $

Slide 161

Slide 161 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } Contig(Contig && other) : size(0), data(nullptr) { std::swap(size, other.size); std::swap(data, other.data); } // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } Contig & operator=(Contig && other) = default; private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(""); tmp = std::move(seq); // ... }

Slide 162

Slide 162 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } Contig(Contig && other) : size(0), data(nullptr) { std::swap(size, other.size); std::swap(data, other.data); } // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } Contig & operator=(Contig && other) = default; private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(""); tmp = std::move(seq); // ... }

Slide 163

Slide 163 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } Contig(Contig && other) : size(0), data(nullptr) { std::swap(size, other.size); std::swap(data, other.data); } // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } Contig & operator=(Contig && other) = delete; private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(""); tmp = std::move(seq); // ... }

Slide 164

Slide 164 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } Contig(Contig && other) : size(0), data(nullptr) { std::swap(size, other.size); std::swap(data, other.data); } // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } Contig & operator=(Contig && other) = delete; private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(""); tmp = std::move(seq); // ... } $ c++ -fsanitize=leak,address tour.cpp && ./a.out overload resolution selected deleted operator '=' $

Slide 165

Slide 165 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } Contig(Contig && other) : size(0), data(nullptr) { std::swap(size, other.size); std::swap(data, other.data); } // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } Contig & operator=(Contig && other) = delete; private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(""); tmp = std::move(seq); // ... }

Slide 166

Slide 166 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } Contig(Contig && other) : size(0), data(nullptr) { std::swap(size, other.size); std::swap(data, other.data); } // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } Contig & operator=(Contig && other) = delete; private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(""); tmp = std::move(seq); // ... } An acceptable implementation might look like this.

Slide 167

Slide 167 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } Contig(Contig && other) : size(0), data(nullptr) { std::swap(size, other.size); std::swap(data, other.data); } // Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); return *this; } Contig & operator=(Contig && other) = delete; private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(""); tmp = std::move(seq); // ... } An acceptable implementation might look like this.

Slide 168

Slide 168 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } Contig(Contig && other) : size(0), data(nullptr) { std::swap(size, other.size); std::swap(data, other.data); } Contig & operator=(Contig && other) { std::swap(size, other.size); std::swap(data, other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(""); tmp = std::move(seq); // ... }

Slide 169

Slide 169 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } Contig(Contig && other) : size(0), data(nullptr) { std::swap(size, other.size); std::swap(data, other.data); } Contig & operator=(Contig && other) { std::swap(size, other.size); std::swap(data, other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(""); tmp = std::move(seq); // ... } $ c++ -fsanitize=leak,address tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG $

Slide 170

Slide 170 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } Contig(Contig && other) : size(0), data(nullptr) { std::swap(size, other.size); std::swap(data, other.data); } Contig & operator=(Contig && other) { std::swap(size, other.size); std::swap(data, other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; };

Slide 171

Slide 171 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } Contig(Contig && other) : size(0), data(nullptr) { std::swap(size, other.size); std::swap(data, other.data); } Contig & operator=(Contig && other) { std::swap(size, other.size); std::swap(data, other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; So now we have provided all the 5 special members and we have a class that supports copy and move properly.

Slide 172

Slide 172 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } Contig(Contig && other) : size(0), data(nullptr) { std::swap(size, other.size); std::swap(data, other.data); } Contig & operator=(Contig && other) { std::swap(size, other.size); std::swap(data, other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; So now we have provided all the 5 special members and we have a class that supports copy and move properly.

Slide 173

Slide 173 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } Contig(Contig && other) : size(0), data(nullptr) { std::swap(size, other.size); std::swap(data, other.data); } Contig & operator=(Contig && other) { std::swap(size, other.size); std::swap(data, other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; So now we have provided all the 5 special members and we have a class that supports copy and move properly. You can even take this idea further, and reduce code duplication by implementing and calling a free-standing swap function that can swap two Contig objects. But we are not going to show that here.

Slide 174

Slide 174 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } Contig(Contig && other) : size(0), data(nullptr) { std::swap(size, other.size); std::swap(data, other.data); } Contig & operator=(Contig && other) { std::swap(size, other.size); std::swap(data, other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; };

Slide 175

Slide 175 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } Contig(Contig && other) : size(0), data(nullptr) { std::swap(size, other.size); std::swap(data, other.data); } Contig & operator=(Contig && other) { std::swap(size, other.size); std::swap(data, other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; };

Slide 176

Slide 176 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } Contig(Contig && other) : size(0), data(nullptr) { std::swap(size, other.size); std::swap(data, other.data); } Contig & operator=(Contig && other) { std::swap(size, other.size); std::swap(data, other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; Another way to implement the move assignment is to copy the content of the other object, and then reset it's state. I call this "gutting" the other object.

Slide 177

Slide 177 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } Contig(Contig && other) : size(0), data(nullptr) { std::swap(size, other.size); std::swap(data, other.data); } Contig & operator=(Contig && other) { std::swap(size, other.size); std::swap(data, other.data); return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; };

Slide 178

Slide 178 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } Contig(Contig && other) : size(0), data(nullptr) { std::swap(size, other.size); std::swap(data, other.data); } Contig & operator=(Contig && other) { delete[] data; size = std::move(other.size); data = std::move(other.data); other.size = 0; other.data = nullptr; return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; };

Slide 179

Slide 179 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } Contig(Contig && other) : size(0), data(nullptr) { std::swap(size, other.size); std::swap(data, other.data); } Contig & operator=(Contig && other) { delete[] data; size = std::move(other.size); data = std::move(other.data); other.size = 0; other.data = nullptr; return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; $ c++ -fsanitize=leak,address tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG $

Slide 180

Slide 180 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } Contig(Contig && other) : size(0), data(nullptr) { std::swap(size, other.size); std::swap(data, other.data); } Contig & operator=(Contig && other) { delete[] data; size = std::move(other.size); data = std::move(other.data); other.size = 0; other.data = nullptr; return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; };

Slide 181

Slide 181 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } Contig(Contig && other) : size(0), data(nullptr) { std::swap(size, other.size); std::swap(data, other.data); } Contig & operator=(Contig && other) { delete[] data; size = std::move(other.size); data = std::move(other.data); other.size = 0; other.data = nullptr; return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; };

Slide 182

Slide 182 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } Contig(Contig && other) : size(0), data(nullptr) { std::swap(size, other.size); std::swap(data, other.data); } Contig & operator=(Contig && other) { delete[] data; size = std::move(other.size); data = std::move(other.data); other.size = 0; other.data = nullptr; return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; Similarly, in the move constructor, we can just do a shallow copy of the other object, but then reset the other object to an empty state.

Slide 183

Slide 183 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) { other.size = 0; other.data = nullptr; } Contig & operator=(Contig && other) { delete[] data; size = std::move(other.size); data = std::move(other.data); other.size = 0; other.data = nullptr; return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; };

Slide 184

Slide 184 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) { other.size = 0; other.data = nullptr; } Contig & operator=(Contig && other) { delete[] data; size = std::move(other.size); data = std::move(other.data); other.size = 0; other.data = nullptr; return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; $ c++ -fsanitize=leak,address tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG $

Slide 185

Slide 185 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) { other.size = 0; other.data = nullptr; } Contig & operator=(Contig && other) { delete[] data; size = std::move(other.size); data = std::move(other.data); other.size = 0; other.data = nullptr; return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; };

Slide 186

Slide 186 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) { other.size = 0; other.data = nullptr; } Contig & operator=(Contig && other) { delete[] data; size = std::move(other.size); data = std::move(other.data); other.size = 0; other.data = nullptr; return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; };

Slide 187

Slide 187 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) { other.size = 0; other.data = nullptr; } Contig & operator=(Contig && other) { delete[] data; size = std::move(other.size); data = std::move(other.data); other.size = 0; other.data = nullptr; return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; It is worth pointing out that std::move() does not have any effect here. Since we are working with primitive types we could have just skipped the "cast" here, and used regular assignment instead.

Slide 188

Slide 188 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) { other.size = 0; other.data = nullptr; } Contig & operator=(Contig && other) { delete[] data; size = std::move(other.size); data = std::move(other.data); other.size = 0; other.data = nullptr; return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; };

Slide 189

Slide 189 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) { other.size = 0; other.data = nullptr; } Contig & operator=(Contig && other) { delete[] data; size = std::move(other.size); data = std::move(other.data); other.size = 0; other.data = nullptr; return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; };

Slide 190

Slide 190 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) { other.size = 0; other.data = nullptr; } Contig & operator=(Contig && other) { delete[] data; size = std::move(other.size); data = std::move(other.data); other.size = 0; other.data = nullptr; return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; Finally, we must consider whether the copy-swap idiom is an acceptable design decision for our job. It is true that this idiom gives you strong exception guarantees, but if are working with really large data structures, then making a copy first does not make sense. Here is an alternative implementation that is worth considering. Requires less memory, it might be faster, but the behavior in case of a memory allocation error is different (and unexpected?).

Slide 191

Slide 191 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) { other.size = 0; other.data = nullptr; } Contig & operator=(Contig && other) { delete[] data; size = std::move(other.size); data = std::move(other.data); other.size = 0; other.data = nullptr; return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; };

Slide 192

Slide 192 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { Contig tmp(other); std::swap(size, tmp.size); std::swap(data, tmp.data); return *this; } Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) { other.size = 0; other.data = nullptr; } Contig & operator=(Contig && other) { delete[] data; size = std::move(other.size); data = std::move(other.data); other.size = 0; other.data = nullptr; return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; };

Slide 193

Slide 193 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { if (&other == this) return *this; delete[] data; data = nullptr; size = 0; data = new int[other.size]; size = other.size; std::copy(other.data, other.data + size, data); return *this; } Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) { other.size = 0; other.data = nullptr; } Contig & operator=(Contig && other) { delete[] data; size = std::move(other.size); data = std::move(other.data); other.size = 0; other.data = nullptr; return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; };

Slide 194

Slide 194 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { if (&other == this) return *this; delete[] data; data = nullptr; size = 0; data = new int[other.size]; size = other.size; std::copy(other.data, other.data + size, data); return *this; } Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) { other.size = 0; other.data = nullptr; } Contig & operator=(Contig && other) { delete[] data; size = std::move(other.size); data = std::move(other.data); other.size = 0; other.data = nullptr; return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; $ c++ -fsanitize=leak,address tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG $

Slide 195

Slide 195 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { if (&other == this) return *this; delete[] data; data = nullptr; size = 0; data = new int[other.size]; size = other.size; std::copy(other.data, other.data + size, data); return *this; } Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) { other.size = 0; other.data = nullptr; } Contig & operator=(Contig && other) { delete[] data; size = std::move(other.size); data = std::move(other.data); other.size = 0; other.data = nullptr; return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; $ c++ -fsanitize=leak,address tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG $

Slide 196

Slide 196 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { if (&other == this) return *this; delete[] data; data = nullptr; size = 0; data = new int[other.size]; size = other.size; std::copy(other.data, other.data + size, data); return *this; } Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) { other.size = 0; other.data = nullptr; } Contig & operator=(Contig && other) { delete[] data; size = std::move(other.size); data = std::move(other.data); other.size = 0; other.data = nullptr; return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; This is also an acceptable implementation of copy- and move-semantics for our Contig class. It all depends on the type of problem that you are trying to solve. Note: If you objects are small and you don't have strict performance requirements then the copy-swap-idiom and move-swap-idiom might be a good idea to get clean code and avoid code duplication. I just wanted to show that there are alternative ways to implement copy and move semantics - and in C++ you can choose depending on what you need. $ c++ -fsanitize=leak,address tour.cpp && ./a.out ACTTCTGTATTGGGTCTTTAATAG $

Slide 197

Slide 197 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { if (&other == this) return *this; delete[] data; data = nullptr; size = 0; data = new int[other.size]; size = other.size; std::copy(other.data, other.data + size, data); return *this; } Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) { other.size = 0; other.data = nullptr; } Contig & operator=(Contig && other) { delete[] data; size = std::move(other.size); data = std::move(other.data); other.size = 0; other.data = nullptr; return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; };

Slide 198

Slide 198 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { if (&other == this) return *this; delete[] data; data = nullptr; size = 0; data = new int[other.size]; size = other.size; std::copy(other.data, other.data + size, data); return *this; } Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) { other.size = 0; other.data = nullptr; } Contig & operator=(Contig && other) { delete[] data; size = std::move(other.size); data = std::move(other.data); other.size = 0; other.data = nullptr; return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; Perhaps you decide that copying Contig objects should not be allowed. Then you can consider...

Slide 199

Slide 199 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) : size(other.size), data(new int[size]) { std::copy(other.data, other.data + size, data); } Contig & operator=(const Contig & other) { if (&other == this) return *this; delete[] data; data = nullptr; size = 0; data = new int[other.size]; size = other.size; std::copy(other.data, other.data + size, data); return *this; } Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) { other.size = 0; other.data = nullptr; } Contig & operator=(Contig && other) { delete[] data; size = std::move(other.size); data = std::move(other.data); other.size = 0; other.data = nullptr; return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; Perhaps you decide that copying Contig objects should not be allowed. Then you can consider...

Slide 200

Slide 200 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) = delete; Contig & operator=(const Contig & other) = delete; Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) { other.size = 0; other.data = nullptr; } Contig & operator=(Contig && other) { delete[] data; size = std::move(other.size); data = std::move(other.data); other.size = 0; other.data = nullptr; return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; };

Slide 201

Slide 201 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) = delete; Contig & operator=(const Contig & other) = delete; Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) { other.size = 0; other.data = nullptr; } Contig & operator=(Contig && other) { delete[] data; size = std::move(other.size); data = std::move(other.data); other.size = 0; other.data = nullptr; return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; };

Slide 202

Slide 202 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) = delete; Contig & operator=(const Contig & other) = delete; Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) { other.size = 0; other.data = nullptr; } Contig & operator=(Contig && other) { delete[] data; size = std::move(other.size); data = std::move(other.data); other.size = 0; other.data = nullptr; return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; };

Slide 203

Slide 203 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) = delete; Contig & operator=(const Contig & other) = delete; Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) { other.size = 0; other.data = nullptr; } Contig & operator=(Contig && other) { delete[] data; size = std::move(other.size); data = std::move(other.data); other.size = 0; other.data = nullptr; return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; So this is an example of stuff you need to do to support move semantics, if you are managing a resource yourself.

Slide 204

Slide 204 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) = delete; Contig & operator=(const Contig & other) = delete; Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) { other.size = 0; other.data = nullptr; } Contig & operator=(Contig && other) { delete[] data; size = std::move(other.size); data = std::move(other.data); other.size = 0; other.data = nullptr; return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; So this is an example of stuff you need to do to support move semantics, if you are managing a resource yourself. ... if you are managing a resource yourself ... ?

Slide 205

Slide 205 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) = delete; Contig & operator=(const Contig & other) = delete; Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) { other.size = 0; other.data = nullptr; } Contig & operator=(Contig && other) { delete[] data; size = std::move(other.size); data = std::move(other.data); other.size = 0; other.data = nullptr; return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; So this is an example of stuff you need to do to support move semantics, if you are managing a resource yourself. ... if you are managing a resource yourself ... ? Of course, in this case, we both can and probably should let a smart pointer or a standard container do the resource management for us. Then a lot of this complexity will just disappear.

Slide 206

Slide 206 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) = delete; Contig & operator=(const Contig & other) = delete; Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) { other.size = 0; other.data = nullptr; } Contig & operator=(Contig && other) { delete[] data; size = std::move(other.size); data = std::move(other.data); other.size = 0; other.data = nullptr; return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; So this is an example of stuff you need to do to support move semantics, if you are managing a resource yourself. ... if you are managing a resource yourself ... ? Of course, in this case, we both can and probably should let a smart pointer or a standard container do the resource management for us. Then a lot of this complexity will just disappear. Let's rewrite this code using unique_ptr instead.

Slide 207

Slide 207 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) = delete; Contig & operator=(const Contig & other) = delete; Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) { other.size = 0; other.data = nullptr; } Contig & operator=(Contig && other) { delete[] data; size = std::move(other.size); data = std::move(other.data); other.size = 0; other.data = nullptr; return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; };

Slide 208

Slide 208 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) = delete; Contig & operator=(const Contig & other) = delete; Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) { other.size = 0; other.data = nullptr; } Contig & operator=(Contig && other) { delete[] data; size = std::move(other.size); data = std::move(other.data); other.size = 0; other.data = nullptr; return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; First change the calculation of the begin/end iterators

Slide 209

Slide 209 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, data); } ~Contig() { delete[] data; } Contig(const Contig & other) = delete; Contig & operator=(const Contig & other) = delete; Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) { other.size = 0; other.data = nullptr; } Contig & operator=(Contig && other) { delete[] data; size = std::move(other.size); data = std::move(other.data); other.size = 0; other.data = nullptr; return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(seq.data, seq.data + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; First change the calculation of the begin/end iterators

Slide 210

Slide 210 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, &data[0]); } ~Contig() { delete[] data; } Contig(const Contig & other) = delete; Contig & operator=(const Contig & other) = delete; Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) { other.size = 0; other.data = nullptr; } Contig & operator=(Contig && other) { delete[] data; size = std::move(other.size); data = std::move(other.data); other.size = 0; other.data = nullptr; return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(&seq.data[0], &seq.data[0] + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; };

Slide 211

Slide 211 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, &data[0]); } ~Contig() { delete[] data; } Contig(const Contig & other) = delete; Contig & operator=(const Contig & other) = delete; Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) { other.size = 0; other.data = nullptr; } Contig & operator=(Contig && other) { delete[] data; size = std::move(other.size); data = std::move(other.data); other.size = 0; other.data = nullptr; return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(&seq.data[0], &seq.data[0] + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; };

Slide 212

Slide 212 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, &data[0]); } ~Contig() { delete[] data; } Contig(const Contig & other) = delete; Contig & operator=(const Contig & other) = delete; Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) { other.size = 0; other.data = nullptr; } Contig & operator=(Contig && other) { delete[] data; size = std::move(other.size); data = std::move(other.data); other.size = 0; other.data = nullptr; return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(&seq.data[0], &seq.data[0] + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; Then change to a std::unique_ptr and therefore also no need for calling delete[] explicitly.

Slide 213

Slide 213 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, &data[0]); } ~Contig() { delete[] data; } Contig(const Contig & other) = delete; Contig & operator=(const Contig & other) = delete; Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) { other.size = 0; other.data = nullptr; } Contig & operator=(Contig && other) { delete[] data; size = std::move(other.size); data = std::move(other.data); other.size = 0; other.data = nullptr; return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(&seq.data[0], &seq.data[0] + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; int * data; }; Then change to a std::unique_ptr and therefore also no need for calling delete[] explicitly.

Slide 214

Slide 214 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, &data[0]); } ~Contig() { } Contig(const Contig & other) = delete; Contig & operator=(const Contig & other) = delete; Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) { other.size = 0; other.data = nullptr; } Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); other.size = 0; other.data = nullptr; return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(&seq.data[0], &seq.data[0] + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; std::unique_ptr data; };

Slide 215

Slide 215 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, &data[0]); } ~Contig() {} Contig(const Contig & other) = delete; Contig & operator=(const Contig & other) = delete; Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) { other.size = 0; other.data = nullptr; } Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); other.size = 0; other.data = nullptr; return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(&seq.data[0], &seq.data[0] + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; std::unique_ptr data; };

Slide 216

Slide 216 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, &data[0]); } ~Contig() {} Contig(const Contig & other) = delete; Contig & operator=(const Contig & other) = delete; Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) { other.size = 0; other.data = nullptr; } Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); other.size = 0; other.data = nullptr; return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(&seq.data[0], &seq.data[0] + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; std::unique_ptr data; };

Slide 217

Slide 217 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, &data[0]); } ~Contig() {} Contig(const Contig & other) = delete; Contig & operator=(const Contig & other) = delete; Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) { other.size = 0; other.data = nullptr; } Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); other.size = 0; other.data = nullptr; return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(&seq.data[0], &seq.data[0] + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; std::unique_ptr data; }; And now, the default destructor is fine. Also the move members are now doing more or less the same as the default implicit move members will do, so we can simplify further.

Slide 218

Slide 218 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, &data[0]); } ~Contig() {} Contig(const Contig & other) = delete; Contig & operator=(const Contig & other) = delete; Contig(Contig && other) : size(std::move(other.size)), data(std::move(other.data)) { other.size = 0; other.data = nullptr; } Contig & operator=(Contig && other) { size = std::move(other.size); data = std::move(other.data); other.size = 0; other.data = nullptr; return *this; } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(&seq.data[0], &seq.data[0] + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; std::unique_ptr data; }; And now, the default destructor is fine. Also the move members are now doing more or less the same as the default implicit move members will do, so we can simplify further.

Slide 219

Slide 219 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, &data[0]); } ~Contig() {} Contig(const Contig & other) = delete; Contig & operator=(const Contig & other) = delete; Contig(Contig && other) = default; Contig & operator=(Contig && other) = default; private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(&seq.data[0], &seq.data[0] + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; std::unique_ptr data; };

Slide 220

Slide 220 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, &data[0]); } ~Contig() {} Contig(const Contig & other) = delete; Contig & operator=(const Contig & other) = delete; Contig(Contig && other) = default; Contig & operator=(Contig && other) = default; private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(&seq.data[0], &seq.data[0] + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; std::unique_ptr data; };

Slide 221

Slide 221 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, &data[0]); } ~Contig() {} Contig(const Contig & other) = delete; Contig & operator=(const Contig & other) = delete; Contig(Contig && other) = default; Contig & operator=(Contig && other) = default; private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(&seq.data[0], &seq.data[0] + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; std::unique_ptr data; };

Slide 222

Slide 222 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, &data[0]); } ~Contig() = default; Contig(const Contig & other) = delete; Contig & operator=(const Contig & other) = delete; Contig(Contig && other) = default; Contig & operator=(Contig && other) = default; private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(&seq.data[0], &seq.data[0] + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; std::unique_ptr data; };

Slide 223

Slide 223 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, &data[0]); } ~Contig() = default; Contig(const Contig & other) = delete; Contig & operator=(const Contig & other) = delete; Contig(Contig && other) = default; Contig & operator=(Contig && other) = default; private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(&seq.data[0], &seq.data[0] + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; std::unique_ptr data; };

Slide 224

Slide 224 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, &data[0]); } ~Contig() = default; Contig(const Contig & other) = delete; Contig & operator=(const Contig & other) = delete; Contig(Contig && other) = default; Contig & operator=(Contig && other) = default; private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(&seq.data[0], &seq.data[0] + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; std::unique_ptr data; };

Slide 225

Slide 225 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, &data[0]); } ~Contig() = default; Contig(const Contig & other) = delete; Contig & operator=(const Contig & other) = delete; Contig(Contig && other) = default; Contig & operator=(Contig && other) = default; private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(&seq.data[0], &seq.data[0] + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; std::unique_ptr data; };

Slide 226

Slide 226 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, &data[0]); } ~Contig() = default; Contig(const Contig & other) = delete; Contig & operator=(const Contig & other) = delete; Contig(Contig && other) = default; Contig & operator=(Contig && other) = default; private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(&seq.data[0], &seq.data[0] + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; std::unique_ptr data; }; Since we are using a std::unique_ptr, the copy members are disabled implicitly by default. So we can really just remove the whole thing if we want to, and we get exactly the behavior we expect and hope for.

Slide 227

Slide 227 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, &data[0]); } ~Contig() = default; Contig(const Contig & other) = delete; Contig & operator=(const Contig & other) = delete; Contig(Contig && other) = default; Contig & operator=(Contig && other) = default; private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(&seq.data[0], &seq.data[0] + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; std::unique_ptr data; }; Since we are using a std::unique_ptr, the copy members are disabled implicitly by default. So we can really just remove the whole thing if we want to, and we get exactly the behavior we expect and hope for.

Slide 228

Slide 228 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, &data[0]); } ~Contig() = default; Contig(const Contig & other) = delete; Contig & operator=(const Contig & other) = delete; Contig(Contig && other) = default; Contig & operator=(Contig && other) = default; private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(&seq.data[0], &seq.data[0] + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; std::unique_ptr data; }; Since we are using a std::unique_ptr, the copy members are disabled implicitly by default. So we can really just remove the whole thing if we want to, and we get exactly the behavior we expect and hope for.

Slide 229

Slide 229 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, &data[0]); } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(&seq.data[0], &seq.data[0] + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; std::unique_ptr data; };

Slide 230

Slide 230 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, &data[0]); } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(&seq.data[0], &seq.data[0] + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; std::unique_ptr data; };

Slide 231

Slide 231 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, &data[0]); } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(&seq.data[0], &seq.data[0] + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; std::unique_ptr data; };

Slide 232

Slide 232 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(new int[size]) { std::copy(str, str + size, &data[0]); } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(&seq.data[0], &seq.data[0] + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; std::unique_ptr data; };

Slide 233

Slide 233 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(std::make_unique(size)) { std::copy(str, str + size, &data[0]); } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(&seq.data[0], &seq.data[0] + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; std::unique_ptr data; };

Slide 234

Slide 234 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(std::make_unique(size)) { std::copy(str, str + size, &data[0]); } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(&seq.data[0], &seq.data[0] + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; std::unique_ptr data; };

Slide 235

Slide 235 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(std::make_unique(size)) { std::copy(str, str + size, &data[0]); } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(&seq.data[0], &seq.data[0] + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; std::unique_ptr data; }; Or, perhaps you would like to use a standard container instead. Eg, std::vector, then you might end up with something even simpler. Standard containers in modern C++ supports both copy and move semantics properly out of the box.

Slide 236

Slide 236 text

class Contig { public: explicit Contig(const char * str) : size(std::strlen(str)), data(std::make_unique(size)) { std::copy(str, str + size, &data[0]); } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(&seq.data[0], &seq.data[0] + seq.size, std::ostream_iterator(out)); return out; } std::size_t size; std::unique_ptr data; }; Or, perhaps you would like to use a standard container instead. Eg, std::vector, then you might end up with something even simpler. Standard containers in modern C++ supports both copy and move semantics properly out of the box.

Slide 237

Slide 237 text

class Contig { public: explicit Contig(const char * str) : data(str, str + std::strlen(str)) { } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(std::cbegin(seq.data), std::cend(seq.data), std::ostream_iterator(out)); return out; } std::vector data; };

Slide 238

Slide 238 text

class Contig { public: explicit Contig(const char * str) : data(str, str + std::strlen(str)) { } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(std::cbegin(seq.data), std::cend(seq.data), std::ostream_iterator(out)); return out; } std::vector data; };

Slide 239

Slide 239 text

class Contig { public: explicit Contig(const char * str) : data(str, str + std::strlen(str)) {} private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(std::cbegin(seq.data), std::cend(seq.data), std::ostream_iterator(out)); return out; } std::vector data; };

Slide 240

Slide 240 text

class Contig { public: explicit Contig(const char * str) : data(str, str + std::strlen(str)) {} private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(std::cbegin(seq.data), std::cend(seq.data), std::ostream_iterator(out)); return out; } std::vector data; };

Slide 241

Slide 241 text

class Contig { public: explicit Contig(const char * str) : data(str, str + std::strlen(str)) {} private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(std::cbegin(seq.data), std::cend(seq.data), std::ostream_iterator(out)); return out; } std::vector data; };

Slide 242

Slide 242 text

class Contig { public: explicit Contig(const char * str) : data(str, str + std::strlen(str)) {} auto begin() const { return std::cbegin(data); } auto end() const { return std::cend(data); } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(std::cbegin(seq), std::cend(seq), std::ostream_iterator(out)); return out; } std::vector data; };

Slide 243

Slide 243 text

class Contig { public: explicit Contig(const char * str) : data(str, str + std::strlen(str)) {} auto begin() const { return std::cbegin(data); } auto end() const { return std::cend(data); } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(std::cbegin(seq), std::cend(seq), std::ostream_iterator(out)); return out; } std::vector data; };

Slide 244

Slide 244 text

class Contig { public: explicit Contig(const char * str) : data(str, str + std::strlen(str)) {} auto begin() const { return std::cbegin(data); } auto end() const { return std::cend(data); } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(std::cbegin(seq), std::cend(seq), std::ostream_iterator(out)); return out; } std::vector data; };

Slide 245

Slide 245 text

class Contig { public: explicit Contig(const char * str) : data(str, str + std::strlen(str)) {} auto begin() const { return std::cbegin(data); } auto end() const { return std::cend(data); } private: friend std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(std::cbegin(seq), std::cend(seq), std::ostream_iterator(out)); return out; } std::vector data; };

Slide 246

Slide 246 text

class Contig { public: explicit Contig(const char * str) : data(str, str + std::strlen(str)) {} auto begin() const { return std::cbegin(data); } auto end() const { return std::cend(data); } private: std::vector data; }; std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(std::cbegin(seq), std::cend(seq), std::ostream_iterator(out)); return out; }

Slide 247

Slide 247 text

class Contig { public: explicit Contig(const char * str) : data(str, str + std::strlen(str)) {} auto begin() const { return std::cbegin(data); } auto end() const { return std::cend(data); } private: std::vector data; }; std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(std::cbegin(seq), std::cend(seq), std::ostream_iterator(out)); return out; }

Slide 248

Slide 248 text

class Contig { public: explicit Contig(const char * str) : data(str, str + std::strlen(str)) {} auto begin() const { return std::cbegin(data); } auto end() const { return std::cend(data); } private: std::vector data; }; std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(std::cbegin(seq), std::cend(seq), std::ostream_iterator(out)); return out; } And if you want to make sure that Contig objects can only be moved around (never copied), then you can add this:

Slide 249

Slide 249 text

class Contig { public: explicit Contig(const char * str) : data(str, str + std::strlen(str)) {} auto begin() const { return std::cbegin(data); } auto end() const { return std::cend(data); } private: std::vector data; }; std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(std::cbegin(seq), std::cend(seq), std::ostream_iterator(out)); return out; } And if you want to make sure that Contig objects can only be moved around (never copied), then you can add this:

Slide 250

Slide 250 text

class Contig { public: explicit Contig(const char * str) : data(str, str + std::strlen(str)) {} Contig(Contig && other) = default; Contig & operator=(Contig && other) = default; auto begin() const { return std::cbegin(data); } auto end() const { return std::cend(data); } private: std::vector data; }; std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(std::cbegin(seq), std::cend(seq), std::ostream_iterator(out)); return out; }

Slide 251

Slide 251 text

class Contig { public: explicit Contig(const char * str) : data(str, str + std::strlen(str)) {} Contig(Contig && other) = default; Contig & operator=(Contig && other) = default; auto begin() const { return std::cbegin(data); } auto end() const { return std::cend(data); } private: std::vector data; }; std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(std::cbegin(seq), std::cend(seq), std::ostream_iterator(out)); return out; }

Slide 252

Slide 252 text

class Contig { public: explicit Contig(const char * str) : data(str, str + std::strlen(str)) {} Contig(Contig && other) = default; Contig & operator=(Contig && other) = default; auto begin() const { return std::cbegin(data); } auto end() const { return std::cend(data); } private: std::vector data; }; std::ostream & operator<<(std::ostream & out, const Contig & seq) { std::copy(std::cbegin(seq), std::cend(seq), std::ostream_iterator(out)); return out; } Nice!

Slide 253

Slide 253 text

!

Slide 254

Slide 254 text

#ifndef MYLIB_HPP_INCLUDED #define MYLIB_HPP_INCLUDED #include #include namespace mylib { class Contig { public: using iterator = std::vector::iterator; using const_iterator = std::vector::const_iterator; explicit Contig(const char * str); Contig(Contig & other) = delete; Contig & operator=(const Contig & other) = delete; Contig(Contig && other) = default; Contig & operator=(Contig && other) = default; const_iterator begin() const; const_iterator end() const; iterator begin(); iterator end(); private: std::vector data_; }; } std::ostream & operator<<( std::ostream & out, const mylib::Contig & seq); #endif $ c++ -c mylib.cpp $ c++ -c demo.cpp $ c++ -o demo mylib.o demo.o $ ./demo ACTTCTGTATTGGGTCTTTAATAG ACTT...TATTGGGTCTTTAATAG $ #include "mylib.hpp" #include #include namespace mylib { Contig::Contig(const char * str) : data_(str, str + std::strlen(str)) { } Contig::const_iterator Contig::begin() const { return std::cbegin(data_); } Contig::const_iterator Contig::end() const { return std::cend(data_); } Contig::iterator Contig::begin() { return std::begin(data_); } Contig::iterator Contig::end() { return std::end(data_); } } std::ostream & operator<<( std::ostream & out, const mylib::Contig & seq) { for (char e : seq) out << e; return out; } #include "mylib.hpp" #include #include #include namespace { void analyze(mylib::Contig && seq) { std::cout << seq << std::endl; // ... mylib::Contig sub("CTG"); mylib::Contig pad("..."); auto at = std::search( std::begin(seq), std::end(seq), std::cbegin(sub), std::cend(sub)); if (at != std::end(seq)) std::copy(std::cbegin(pad), std::cend(pad), at); std::cout << seq << std::endl; // ... } mylib::Contig extract() { // ... return mylib::Contig("ACTTCTGTATTGGGTCTTTAATAG"); } } int main() { analyze(extract()); } mylib.hpp mylib.cpp demo.cpp Example of a more complete Contig class

Slide 255

Slide 255 text

https://www.slideshare.net/ripplelabs/howard-hinnant-accu2014 It is hard-ish to remember when and what a compiler declares implicitly or not. And once you know it by heart, you can be sure that one of your damn colleagues has forgotten it. Probably a good idea to be explicit and verbose when doing these things. At the ACCU 2014 keynote, I seem to recall that Howard Hinnant admitted that he kept a copy of this particular table next to his workstation. (Howard was the lead designer and implementor of rvalue references and move semantics in the C++11 standard.)

Slide 256

Slide 256 text

Historically, lvalues and rvalues were so-called because they could appear on the left- and right-hand side of an assignment (although this is no longer generally true); glvalues are “generalized” lvalues, prvalues are “pure” rvalues, and xvalues are “eXpiring” lvalues. Despite their names, these terms classify expressions, not values.

Slide 257

Slide 257 text

“Every block of stone has a statue inside it and it is the task of the sculptor to discover it.” Michelangelo “As a programmer, your main job is to reduce flexibility and options in a computer system until the point that it becomes useful and valuable for others.” Olve