SlideShare a Scribd company logo
Modern C++ Explained : Move Semantics
February 5, 2018
by Olve Maudal
In this session I will try to explain rvalue references
(aka refref) and move semantics. Here is a code
snippet to get us started…
#include <iostream>
#include <string>
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…
#include <iostream>
#include <string>
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…
#include <iostream>
#include <string>
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…
#include <iostream>
#include <string>
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.
#include <iostream>
#include <string>
void analyze(std::string seq)
{
std::cout << seq << std::endl;
// ...
}
int main()
{
std::string seq = "ACTTCTGTATTGGGTCTTTAATAG";
analyze(seq);
}
#include <iostream>
#include <string>
void analyze(std::string seq)
{
std::cout << seq << std::endl;
// ...
}
int main()
{
std::string seq = "ACTTCTGTATTGGGTCTTTAATAG";
analyze(seq);
}
#include <iostream>
#include <string>
void analyze(const std::string & seq)
{
std::cout << seq << std::endl;
// ...
}
int main()
{
std::string seq = "ACTTCTGTATTGGGTCTTTAATAG";
analyze(seq);
}
#include <iostream>
#include <string>
void analyze(const std::string & seq)
{
std::cout << seq << std::endl;
// ...
}
int main()
{
std::string seq = "ACTTCTGTATTGGGTCTTTAATAG";
analyze(seq);
}
#include <iostream>
#include <string>
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…
#include <iostream>
#include <string>
void analyze(const std::string & seq)
{
std::cout << seq << std::endl;
// ...
}
int main()
{
std::string seq = "ACTTCTGTATTGGGTCTTTAATAG";
analyze(seq);
}
#include <iostream>
#include <string>
void analyze(const std::string & seq)
{
std::cout << seq << std::endl;
// ...
}
int main()
{
std::string seq = "ACTTCTGTATTGGGTCTTTAATAG";
analyze(seq);
}
#include <iostream>
#include <string>
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);
}
#include <iostream>
#include <string>
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
$
#include <iostream>
#include <string>
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
$
#include <iostream>
#include <string>
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?
#include <iostream>
#include <string>
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?
#include <iostream>
#include <string>
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);
}
#include <iostream>
#include <string>
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);
}
#include <iostream>
#include <string>
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);
}
#include <iostream>
#include <string>
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);
}
#include <iostream>
#include <string>
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);
}
#include <iostream>
#include <string>
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);
}
#include <iostream>
#include <string>
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);
}
#include <iostream>
#include <string>
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);
}
#include <iostream>
#include <string>
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);
}
#include <iostream>
#include <string>
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.
#include <iostream>
#include <string>
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…
#include <iostream>
#include <string>
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);
}
#include <iostream>
#include <string>
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);
}
#include <iostream>
#include <string>
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);
}
#include <iostream>
#include <string>
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);
}
#include <iostream>
#include <string>
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);
}
#include <iostream>
#include <string>
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
$
#include <iostream>
#include <string>
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);
}
#include <iostream>
#include <string>
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);
}
#include <iostream>
#include <string>
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());
}
#include <iostream>
#include <string>
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());
}
#include <iostream>
#include <string>
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.
#include <iostream>
#include <string>
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.
#include <iostream>
#include <string>
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.
#include <iostream>
#include <string>
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
#include <iostream>
#include <string>
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?
#include <iostream>
#include <string>
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
#include <iostream>
#include <string>
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());
}
#include <iostream>
#include <string>
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());
}
#include <iostream>
#include <string>
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());
}
#include <iostream>
#include <string>
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());
}
#include <iostream>
#include <string>
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
$
#include <iostream>
#include <string>
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.
#include <iostream>
#include <string>
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?
#include <iostream>
#include <string>
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?
#include <iostream>
#include <string>
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?
#include <iostream>
#include <string>
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());
}
#include <iostream>
#include <string>
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());
}
#include <iostream>
#include <string>
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);
}
#include <iostream>
#include <string>
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 &&’
$
#include <iostream>
#include <string>
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.
#include <iostream>
#include <string>
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);
}
#include <iostream>
#include <string>
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()
#include <iostream>
#include <string>
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()
#include <iostream>
#include <string>
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()
#include <iostream>
#include <string>
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));
}
#include <iostream>
#include <string>
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
$
#include <iostream>
#include <string>
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.”
#include <iostream>
#include <string>
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)
#include <iostream>
#include <string>
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)
#include <iostream>
#include <string>
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));
}
#include <iostream>
#include <string>
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));
}
#include <iostream>
#include <string>
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));
}
#include <iostream>
#include <string>
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));
}
#include <iostream>
#include <string>
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));
}
#include <iostream>
#include <string>
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));
}
#include <iostream>
#include <string>
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.
#include <iostream>
#include <string>
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).
#include <iostream>
#include <string>
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));
}
#include <iostream>
#include <string>
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));
}
#include <iostream>
#include <algorithm>
#include <cstring>
#include <iterator>
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<char>(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));
}
#include <iostream>
#include <algorithm>
#include <cstring>
#include <iterator>
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<char>(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));
}
#include <algorithm>
#include <cstring>
#include <iostream>
#include <iterator>
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<char>(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));
}
#include <algorithm>
#include <cstring>
#include <iostream>
#include <iterator>
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<char>(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));
}
#include <algorithm>
#include <cstring>
#include <iostream>
#include <iterator>
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<char>(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
$
#include <algorithm>
#include <cstring>
#include <iostream>
#include <iterator>
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<char>(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.
#include <algorithm>
#include <cstring>
#include <iostream>
#include <iterator>
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<char>(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.
#include <algorithm>
#include <cstring>
#include <iostream>
#include <iterator>
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<char>(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.
#include <algorithm>
#include <cstring>
#include <iostream>
#include <iterator>
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<char>(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));
}
#include <algorithm>
#include <cstring>
#include <iostream>
#include <iterator>
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<char>(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
$
#include <algorithm>
#include <cstring>
#include <iostream>
#include <iterator>
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<char>(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.
#include <algorithm>
#include <cstring>
#include <iostream>
#include <iterator>
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<char>(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.
#include <algorithm>
#include <cstring>
#include <iostream>
#include <iterator>
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<char>(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));
}
#include <algorithm>
#include <cstring>
#include <iostream>
#include <iterator>
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<char>(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));
}
#include <algorithm>
#include <cstring>
#include <iostream>
#include <iterator>
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<char>(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
#include <algorithm>
#include <cstring>
#include <iostream>
#include <iterator>
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<char>(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
#include <algorithm>
#include <cstring>
#include <iostream>
#include <iterator>
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<char>(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));
}
#include <algorithm>
#include <cstring>
#include <iostream>
#include <iterator>
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<char>(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.
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<char>(out));
return out;
}
std::size_t size;
int * data;
};
void analyze(Contig && seq)
{
std::cout << seq << std::endl;
// ...
}
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<char>(out));
return out;
}
std::size_t size;
int * data;
};
void analyze(Contig && seq)
{
std::cout << seq << std::endl;
// ...
}
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<char>(out));
return out;
}
std::size_t size;
int * data;
};
void analyze(Contig && seq)
{
std::cout << seq << std::endl;
// ...
}
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<char>(out));
return out;
}
std::size_t size;
int * data;
};
void analyze(Contig && seq)
{
std::cout << seq << std::endl;
// ...
}
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<char>(out));
return out;
}
std::size_t size;
int * data;
};
void analyze(Contig && seq)
{
std::cout << seq << std::endl;
// ...
}
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<char>(out));
return out;
}
std::size_t size;
int * data;
};
void analyze(Contig && seq)
{
std::cout << seq << std::endl;
// ...
}
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<char>(out));
return out;
}
std::size_t size;
int * data;
};
void analyze(Contig && seq)
{
std::cout << seq << std::endl;
// ...
}
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<char>(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
$
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<char>(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.
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<char>(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.
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<char>(out));
return out;
}
std::size_t size;
int * data;
};
void analyze(Contig && seq)
{
std::cout << seq << std::endl;
// ...
}
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<char>(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
$
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<char>(out));
return out;
}
std::size_t size;
int * data;
};
void analyze(Contig && seq)
{
std::cout << seq << std::endl;
// ...
}
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<char>(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…
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<char>(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…
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<char>(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…
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<char>(out));
return out;
}
std::size_t size;
int * data;
};
void analyze(Contig && seq)
{
std::cout << seq << std::endl;
// ...
Contig tmp(seq);
// ...
}
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<char>(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
$
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<char>(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.
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<char>(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.
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<char>(out));
return out;
}
std::size_t size;
int * data;
};
void analyze(Contig && seq)
{
std::cout << seq << std::endl;
// ...
Contig tmp(seq);
// ...
}
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<char>(out));
return out;
}
std::size_t size;
int * data;
};
void analyze(Contig && seq)
{
std::cout << seq << std::endl;
// ...
Contig tmp(seq);
// ...
}
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<char>(out));
return out;
}
std::size_t size;
int * data;
};
void analyze(Contig && seq)
{
std::cout << seq << std::endl;
// ...
Contig tmp(seq);
// ...
}
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<char>(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
$
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<char>(out));
return out;
}
std::size_t size;
int * data;
};
void analyze(Contig && seq)
{
std::cout << seq << std::endl;
// ...
Contig tmp(seq);
// ...
}
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<char>(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...
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<char>(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...
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<char>(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...
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<char>(out));
return out;
}
std::size_t size;
int * data;
};
void analyze(Contig && seq)
{
std::cout << seq << std::endl;
// ...
Contig tmp(seq);
seq = tmp;
// ...
}
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<char>(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
$
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<char>(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.
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<char>(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.
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<char>(out));
return out;
}
std::size_t size;
int * data;
};
void analyze(Contig && seq)
{
std::cout << seq << std::endl;
// ...
Contig tmp(seq);
seq = tmp;
// ...
}
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<char>(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
$
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<char>(out));
return out;
}
std::size_t size;
int * data;
};
void analyze(Contig && seq)
{
std::cout << seq << std::endl;
// ...
Contig tmp(seq);
seq = tmp;
// ...
}
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<char>(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.
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<char>(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.
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<char>(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.
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<char>(out));
return out;
}
std::size_t size;
int * data;
};
void analyze(Contig && seq)
{
std::cout << seq << std::endl;
// ...
Contig tmp(std::move(seq));
// ...
}
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<char>(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
$
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)
Modern C++ Explained: Move Semantics (Feb 2018)

More Related Content

What's hot

C++ 11 Features
C++ 11 FeaturesC++ 11 Features
C++ 11 Features
Jan Rüegg
 
Basic Programs of C++
Basic Programs of C++Basic Programs of C++
Basic Programs of C++
Bharat Kalia
 
C++ Programming Course
C++ Programming CourseC++ Programming Course
C++ Programming Course
Dennis Chang
 
Introduction to cpp
Introduction to cppIntroduction to cpp
Introduction to cpp
Nilesh Dalvi
 
OOP in C++
OOP in C++OOP in C++
OOP in C++
ppd1961
 
88 c-programs
88 c-programs88 c-programs
88 c-programs
Leandro Schenone
 
Algorithmique et programmation en Pascal (résumé)
Algorithmique et programmation en Pascal (résumé)Algorithmique et programmation en Pascal (résumé)
Algorithmique et programmation en Pascal (résumé)
salah fenni
 
C#のキモイ高速プログラミング
C#のキモイ高速プログラミングC#のキモイ高速プログラミング
C#のキモイ高速プログラミング
Kaisei Sunaga
 
C++11
C++11C++11
C++ questions And Answer
C++ questions And AnswerC++ questions And Answer
C++ questions And Answer
lavparmar007
 
Python :variable types
Python :variable typesPython :variable types
Python :variable types
S.M. Salaquzzaman
 
Introduction to Kotlin coroutines
Introduction to Kotlin coroutinesIntroduction to Kotlin coroutines
Introduction to Kotlin coroutines
Roman Elizarov
 
C++ programming function
C++ programming functionC++ programming function
C++ programming function
Vishalini Mugunen
 
An introduction to Rust: the modern programming language to develop safe and ...
An introduction to Rust: the modern programming language to develop safe and ...An introduction to Rust: the modern programming language to develop safe and ...
An introduction to Rust: the modern programming language to develop safe and ...
Claudio Capobianco
 
Python Course for Beginners
Python Course for BeginnersPython Course for Beginners
Python Course for Beginners
Nandakumar P
 
Modern c++ (C++ 11/14)
Modern c++ (C++ 11/14)Modern c++ (C++ 11/14)
Modern c++ (C++ 11/14)
Geeks Anonymes
 
Modern C++
Modern C++Modern C++
Modern C++
Richard Thomson
 
Processingによるプログラミング入門 第6回
Processingによるプログラミング入門 第6回Processingによるプログラミング入門 第6回
Processingによるプログラミング入門 第6回Ryo Suzuki
 
Pointers in C Language
Pointers in C LanguagePointers in C Language
Pointers in C Language
madan reddy
 

What's hot (20)

C++ 11 Features
C++ 11 FeaturesC++ 11 Features
C++ 11 Features
 
Basic Programs of C++
Basic Programs of C++Basic Programs of C++
Basic Programs of C++
 
C++ Programming Course
C++ Programming CourseC++ Programming Course
C++ Programming Course
 
Introduction to cpp
Introduction to cppIntroduction to cpp
Introduction to cpp
 
OOP in C++
OOP in C++OOP in C++
OOP in C++
 
88 c-programs
88 c-programs88 c-programs
88 c-programs
 
Algorithmique et programmation en Pascal (résumé)
Algorithmique et programmation en Pascal (résumé)Algorithmique et programmation en Pascal (résumé)
Algorithmique et programmation en Pascal (résumé)
 
C#のキモイ高速プログラミング
C#のキモイ高速プログラミングC#のキモイ高速プログラミング
C#のキモイ高速プログラミング
 
C++11
C++11C++11
C++11
 
C++ questions And Answer
C++ questions And AnswerC++ questions And Answer
C++ questions And Answer
 
Python :variable types
Python :variable typesPython :variable types
Python :variable types
 
Introduction to Kotlin coroutines
Introduction to Kotlin coroutinesIntroduction to Kotlin coroutines
Introduction to Kotlin coroutines
 
C++ programming function
C++ programming functionC++ programming function
C++ programming function
 
An introduction to Rust: the modern programming language to develop safe and ...
An introduction to Rust: the modern programming language to develop safe and ...An introduction to Rust: the modern programming language to develop safe and ...
An introduction to Rust: the modern programming language to develop safe and ...
 
Python Course for Beginners
Python Course for BeginnersPython Course for Beginners
Python Course for Beginners
 
Correction
CorrectionCorrection
Correction
 
Modern c++ (C++ 11/14)
Modern c++ (C++ 11/14)Modern c++ (C++ 11/14)
Modern c++ (C++ 11/14)
 
Modern C++
Modern C++Modern C++
Modern C++
 
Processingによるプログラミング入門 第6回
Processingによるプログラミング入門 第6回Processingによるプログラミング入門 第6回
Processingによるプログラミング入門 第6回
 
Pointers in C Language
Pointers in C LanguagePointers in C Language
Pointers in C Language
 

Similar to Modern C++ Explained: Move Semantics (Feb 2018)

Евгений Крутько, Многопоточные вычисления, современный подход.
Евгений Крутько, Многопоточные вычисления, современный подход.Евгений Крутько, Многопоточные вычисления, современный подход.
Евгений Крутько, Многопоточные вычисления, современный подход.
Platonov Sergey
 
I want help in the following C++ programming task. Please do coding .pdf
I want help in the following C++ programming task. Please do coding .pdfI want help in the following C++ programming task. Please do coding .pdf
I want help in the following C++ programming task. Please do coding .pdf
bermanbeancolungak45
 
Assignment 13assg-13.cppAssignment 13assg-13.cpp   @auth.docx
Assignment 13assg-13.cppAssignment 13assg-13.cpp   @auth.docxAssignment 13assg-13.cppAssignment 13assg-13.cpp   @auth.docx
Assignment 13assg-13.cppAssignment 13assg-13.cpp   @auth.docx
braycarissa250
 
Javascript built in String Functions
Javascript built in String FunctionsJavascript built in String Functions
Javascript built in String Functions
Avanitrambadiya
 
This is a c++ binary search program I worked so far but still cant g.pdf
This is a c++ binary search program I worked so far but still cant g.pdfThis is a c++ binary search program I worked so far but still cant g.pdf
This is a c++ binary search program I worked so far but still cant g.pdf
kostikjaylonshaewe47
 
Unit 6 pointers
Unit 6   pointersUnit 6   pointers
Unit 6 pointers
George Erfesoglou
 
httplinux.die.netman3execfork() creates a new process by.docx
httplinux.die.netman3execfork() creates a new process by.docxhttplinux.die.netman3execfork() creates a new process by.docx
httplinux.die.netman3execfork() creates a new process by.docx
adampcarr67227
 
Alexey Tsoy Meta Programming in C++ 16.11.17
Alexey Tsoy Meta Programming in C++ 16.11.17Alexey Tsoy Meta Programming in C++ 16.11.17
Alexey Tsoy Meta Programming in C++ 16.11.17
LogeekNightUkraine
 
(Slightly) Smarter Smart Pointers
(Slightly) Smarter Smart Pointers(Slightly) Smarter Smart Pointers
(Slightly) Smarter Smart Pointers
Carlo Pescio
 
2 BytesC++ course_2014_c3_ function basics&parameters and overloading
2 BytesC++ course_2014_c3_ function basics&parameters and overloading2 BytesC++ course_2014_c3_ function basics&parameters and overloading
2 BytesC++ course_2014_c3_ function basics&parameters and overloading
kinan keshkeh
 
Start with the inclusion of libraries#include iostream .docx
 Start with the inclusion of libraries#include iostream .docx Start with the inclusion of libraries#include iostream .docx
Start with the inclusion of libraries#include iostream .docx
MARRY7
 
array, function, pointer, pattern matching
array, function, pointer, pattern matchingarray, function, pointer, pattern matching
array, function, pointer, pattern matching
Shakila Mahjabin
 
Spark with Elasticsearch - umd version 2014
Spark with Elasticsearch - umd version 2014Spark with Elasticsearch - umd version 2014
Spark with Elasticsearch - umd version 2014
Holden Karau
 
The STL
The STLThe STL
The STL
adil raja
 
Were writing code for a project that dynamically allocates an arra.pdf
Were writing code for a project that dynamically allocates an arra.pdfWere writing code for a project that dynamically allocates an arra.pdf
Were writing code for a project that dynamically allocates an arra.pdf
fsenterprises
 
DATASTRUCTURES PPTS PREPARED BY M V BRAHMANANDA REDDY
DATASTRUCTURES PPTS PREPARED BY M V BRAHMANANDA REDDYDATASTRUCTURES PPTS PREPARED BY M V BRAHMANANDA REDDY
DATASTRUCTURES PPTS PREPARED BY M V BRAHMANANDA REDDY
Malikireddy Bramhananda Reddy
 
Category theory, Monads, and Duality in the world of (BIG) Data
Category theory, Monads, and Duality in the world of (BIG) DataCategory theory, Monads, and Duality in the world of (BIG) Data
Category theory, Monads, and Duality in the world of (BIG) Data
greenwop
 
C++11 - STL Additions
C++11 - STL AdditionsC++11 - STL Additions
C++11 - STL Additions
GlobalLogic Ukraine
 
strings
stringsstrings
strings
teach4uin
 
Bind me if you can
Bind me if you canBind me if you can
Bind me if you can
Ovidiu Farauanu
 

Similar to Modern C++ Explained: Move Semantics (Feb 2018) (20)

Евгений Крутько, Многопоточные вычисления, современный подход.
Евгений Крутько, Многопоточные вычисления, современный подход.Евгений Крутько, Многопоточные вычисления, современный подход.
Евгений Крутько, Многопоточные вычисления, современный подход.
 
I want help in the following C++ programming task. Please do coding .pdf
I want help in the following C++ programming task. Please do coding .pdfI want help in the following C++ programming task. Please do coding .pdf
I want help in the following C++ programming task. Please do coding .pdf
 
Assignment 13assg-13.cppAssignment 13assg-13.cpp   @auth.docx
Assignment 13assg-13.cppAssignment 13assg-13.cpp   @auth.docxAssignment 13assg-13.cppAssignment 13assg-13.cpp   @auth.docx
Assignment 13assg-13.cppAssignment 13assg-13.cpp   @auth.docx
 
Javascript built in String Functions
Javascript built in String FunctionsJavascript built in String Functions
Javascript built in String Functions
 
This is a c++ binary search program I worked so far but still cant g.pdf
This is a c++ binary search program I worked so far but still cant g.pdfThis is a c++ binary search program I worked so far but still cant g.pdf
This is a c++ binary search program I worked so far but still cant g.pdf
 
Unit 6 pointers
Unit 6   pointersUnit 6   pointers
Unit 6 pointers
 
httplinux.die.netman3execfork() creates a new process by.docx
httplinux.die.netman3execfork() creates a new process by.docxhttplinux.die.netman3execfork() creates a new process by.docx
httplinux.die.netman3execfork() creates a new process by.docx
 
Alexey Tsoy Meta Programming in C++ 16.11.17
Alexey Tsoy Meta Programming in C++ 16.11.17Alexey Tsoy Meta Programming in C++ 16.11.17
Alexey Tsoy Meta Programming in C++ 16.11.17
 
(Slightly) Smarter Smart Pointers
(Slightly) Smarter Smart Pointers(Slightly) Smarter Smart Pointers
(Slightly) Smarter Smart Pointers
 
2 BytesC++ course_2014_c3_ function basics&parameters and overloading
2 BytesC++ course_2014_c3_ function basics&parameters and overloading2 BytesC++ course_2014_c3_ function basics&parameters and overloading
2 BytesC++ course_2014_c3_ function basics&parameters and overloading
 
Start with the inclusion of libraries#include iostream .docx
 Start with the inclusion of libraries#include iostream .docx Start with the inclusion of libraries#include iostream .docx
Start with the inclusion of libraries#include iostream .docx
 
array, function, pointer, pattern matching
array, function, pointer, pattern matchingarray, function, pointer, pattern matching
array, function, pointer, pattern matching
 
Spark with Elasticsearch - umd version 2014
Spark with Elasticsearch - umd version 2014Spark with Elasticsearch - umd version 2014
Spark with Elasticsearch - umd version 2014
 
The STL
The STLThe STL
The STL
 
Were writing code for a project that dynamically allocates an arra.pdf
Were writing code for a project that dynamically allocates an arra.pdfWere writing code for a project that dynamically allocates an arra.pdf
Were writing code for a project that dynamically allocates an arra.pdf
 
DATASTRUCTURES PPTS PREPARED BY M V BRAHMANANDA REDDY
DATASTRUCTURES PPTS PREPARED BY M V BRAHMANANDA REDDYDATASTRUCTURES PPTS PREPARED BY M V BRAHMANANDA REDDY
DATASTRUCTURES PPTS PREPARED BY M V BRAHMANANDA REDDY
 
Category theory, Monads, and Duality in the world of (BIG) Data
Category theory, Monads, and Duality in the world of (BIG) DataCategory theory, Monads, and Duality in the world of (BIG) Data
Category theory, Monads, and Duality in the world of (BIG) Data
 
C++11 - STL Additions
C++11 - STL AdditionsC++11 - STL Additions
C++11 - STL Additions
 
strings
stringsstrings
strings
 
Bind me if you can
Bind me if you canBind me if you can
Bind me if you can
 

Recently uploaded

What is Augmented Reality Image Tracking
What is Augmented Reality Image TrackingWhat is Augmented Reality Image Tracking
What is Augmented Reality Image Tracking
pavan998932
 
Measures in SQL (SIGMOD 2024, Santiago, Chile)
Measures in SQL (SIGMOD 2024, Santiago, Chile)Measures in SQL (SIGMOD 2024, Santiago, Chile)
Measures in SQL (SIGMOD 2024, Santiago, Chile)
Julian Hyde
 
Using Xen Hypervisor for Functional Safety
Using Xen Hypervisor for Functional SafetyUsing Xen Hypervisor for Functional Safety
Using Xen Hypervisor for Functional Safety
Ayan Halder
 
Hand Rolled Applicative User Validation Code Kata
Hand Rolled Applicative User ValidationCode KataHand Rolled Applicative User ValidationCode Kata
Hand Rolled Applicative User Validation Code Kata
Philip Schwarz
 
Unveiling the Advantages of Agile Software Development.pdf
Unveiling the Advantages of Agile Software Development.pdfUnveiling the Advantages of Agile Software Development.pdf
Unveiling the Advantages of Agile Software Development.pdf
brainerhub1
 
Revolutionizing Visual Effects Mastering AI Face Swaps.pdf
Revolutionizing Visual Effects Mastering AI Face Swaps.pdfRevolutionizing Visual Effects Mastering AI Face Swaps.pdf
Revolutionizing Visual Effects Mastering AI Face Swaps.pdf
Undress Baby
 
2024 eCommerceDays Toulouse - Sylius 2.0.pdf
2024 eCommerceDays Toulouse - Sylius 2.0.pdf2024 eCommerceDays Toulouse - Sylius 2.0.pdf
2024 eCommerceDays Toulouse - Sylius 2.0.pdf
Łukasz Chruściel
 
Essentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FMEEssentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FME
Safe Software
 
UI5con 2024 - Keynote: Latest News about UI5 and it’s Ecosystem
UI5con 2024 - Keynote: Latest News about UI5 and it’s EcosystemUI5con 2024 - Keynote: Latest News about UI5 and it’s Ecosystem
UI5con 2024 - Keynote: Latest News about UI5 and it’s Ecosystem
Peter Muessig
 
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdf
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdfAutomated software refactoring with OpenRewrite and Generative AI.pptx.pdf
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdf
timtebeek1
 
GreenCode-A-VSCode-Plugin--Dario-Jurisic
GreenCode-A-VSCode-Plugin--Dario-JurisicGreenCode-A-VSCode-Plugin--Dario-Jurisic
GreenCode-A-VSCode-Plugin--Dario-Jurisic
Green Software Development
 
KuberTENes Birthday Bash Guadalajara - Introducción a Argo CD
KuberTENes Birthday Bash Guadalajara - Introducción a Argo CDKuberTENes Birthday Bash Guadalajara - Introducción a Argo CD
KuberTENes Birthday Bash Guadalajara - Introducción a Argo CD
rodomar2
 
UI5con 2024 - Boost Your Development Experience with UI5 Tooling Extensions
UI5con 2024 - Boost Your Development Experience with UI5 Tooling ExtensionsUI5con 2024 - Boost Your Development Experience with UI5 Tooling Extensions
UI5con 2024 - Boost Your Development Experience with UI5 Tooling Extensions
Peter Muessig
 
Oracle Database 19c New Features for DBAs and Developers.pptx
Oracle Database 19c New Features for DBAs and Developers.pptxOracle Database 19c New Features for DBAs and Developers.pptx
Oracle Database 19c New Features for DBAs and Developers.pptx
Remote DBA Services
 
Graspan: A Big Data System for Big Code Analysis
Graspan: A Big Data System for Big Code AnalysisGraspan: A Big Data System for Big Code Analysis
Graspan: A Big Data System for Big Code Analysis
Aftab Hussain
 
Transform Your Communication with Cloud-Based IVR Solutions
Transform Your Communication with Cloud-Based IVR SolutionsTransform Your Communication with Cloud-Based IVR Solutions
Transform Your Communication with Cloud-Based IVR Solutions
TheSMSPoint
 
GraphSummit Paris - The art of the possible with Graph Technology
GraphSummit Paris - The art of the possible with Graph TechnologyGraphSummit Paris - The art of the possible with Graph Technology
GraphSummit Paris - The art of the possible with Graph Technology
Neo4j
 
Neo4j - Product Vision and Knowledge Graphs - GraphSummit Paris
Neo4j - Product Vision and Knowledge Graphs - GraphSummit ParisNeo4j - Product Vision and Knowledge Graphs - GraphSummit Paris
Neo4j - Product Vision and Knowledge Graphs - GraphSummit Paris
Neo4j
 
Neo4j - Product Vision and Knowledge Graphs - GraphSummit Paris
Neo4j - Product Vision and Knowledge Graphs - GraphSummit ParisNeo4j - Product Vision and Knowledge Graphs - GraphSummit Paris
Neo4j - Product Vision and Knowledge Graphs - GraphSummit Paris
Neo4j
 
LORRAINE ANDREI_LEQUIGAN_HOW TO USE WHATSAPP.pptx
LORRAINE ANDREI_LEQUIGAN_HOW TO USE WHATSAPP.pptxLORRAINE ANDREI_LEQUIGAN_HOW TO USE WHATSAPP.pptx
LORRAINE ANDREI_LEQUIGAN_HOW TO USE WHATSAPP.pptx
lorraineandreiamcidl
 

Recently uploaded (20)

What is Augmented Reality Image Tracking
What is Augmented Reality Image TrackingWhat is Augmented Reality Image Tracking
What is Augmented Reality Image Tracking
 
Measures in SQL (SIGMOD 2024, Santiago, Chile)
Measures in SQL (SIGMOD 2024, Santiago, Chile)Measures in SQL (SIGMOD 2024, Santiago, Chile)
Measures in SQL (SIGMOD 2024, Santiago, Chile)
 
Using Xen Hypervisor for Functional Safety
Using Xen Hypervisor for Functional SafetyUsing Xen Hypervisor for Functional Safety
Using Xen Hypervisor for Functional Safety
 
Hand Rolled Applicative User Validation Code Kata
Hand Rolled Applicative User ValidationCode KataHand Rolled Applicative User ValidationCode Kata
Hand Rolled Applicative User Validation Code Kata
 
Unveiling the Advantages of Agile Software Development.pdf
Unveiling the Advantages of Agile Software Development.pdfUnveiling the Advantages of Agile Software Development.pdf
Unveiling the Advantages of Agile Software Development.pdf
 
Revolutionizing Visual Effects Mastering AI Face Swaps.pdf
Revolutionizing Visual Effects Mastering AI Face Swaps.pdfRevolutionizing Visual Effects Mastering AI Face Swaps.pdf
Revolutionizing Visual Effects Mastering AI Face Swaps.pdf
 
2024 eCommerceDays Toulouse - Sylius 2.0.pdf
2024 eCommerceDays Toulouse - Sylius 2.0.pdf2024 eCommerceDays Toulouse - Sylius 2.0.pdf
2024 eCommerceDays Toulouse - Sylius 2.0.pdf
 
Essentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FMEEssentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FME
 
UI5con 2024 - Keynote: Latest News about UI5 and it’s Ecosystem
UI5con 2024 - Keynote: Latest News about UI5 and it’s EcosystemUI5con 2024 - Keynote: Latest News about UI5 and it’s Ecosystem
UI5con 2024 - Keynote: Latest News about UI5 and it’s Ecosystem
 
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdf
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdfAutomated software refactoring with OpenRewrite and Generative AI.pptx.pdf
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdf
 
GreenCode-A-VSCode-Plugin--Dario-Jurisic
GreenCode-A-VSCode-Plugin--Dario-JurisicGreenCode-A-VSCode-Plugin--Dario-Jurisic
GreenCode-A-VSCode-Plugin--Dario-Jurisic
 
KuberTENes Birthday Bash Guadalajara - Introducción a Argo CD
KuberTENes Birthday Bash Guadalajara - Introducción a Argo CDKuberTENes Birthday Bash Guadalajara - Introducción a Argo CD
KuberTENes Birthday Bash Guadalajara - Introducción a Argo CD
 
UI5con 2024 - Boost Your Development Experience with UI5 Tooling Extensions
UI5con 2024 - Boost Your Development Experience with UI5 Tooling ExtensionsUI5con 2024 - Boost Your Development Experience with UI5 Tooling Extensions
UI5con 2024 - Boost Your Development Experience with UI5 Tooling Extensions
 
Oracle Database 19c New Features for DBAs and Developers.pptx
Oracle Database 19c New Features for DBAs and Developers.pptxOracle Database 19c New Features for DBAs and Developers.pptx
Oracle Database 19c New Features for DBAs and Developers.pptx
 
Graspan: A Big Data System for Big Code Analysis
Graspan: A Big Data System for Big Code AnalysisGraspan: A Big Data System for Big Code Analysis
Graspan: A Big Data System for Big Code Analysis
 
Transform Your Communication with Cloud-Based IVR Solutions
Transform Your Communication with Cloud-Based IVR SolutionsTransform Your Communication with Cloud-Based IVR Solutions
Transform Your Communication with Cloud-Based IVR Solutions
 
GraphSummit Paris - The art of the possible with Graph Technology
GraphSummit Paris - The art of the possible with Graph TechnologyGraphSummit Paris - The art of the possible with Graph Technology
GraphSummit Paris - The art of the possible with Graph Technology
 
Neo4j - Product Vision and Knowledge Graphs - GraphSummit Paris
Neo4j - Product Vision and Knowledge Graphs - GraphSummit ParisNeo4j - Product Vision and Knowledge Graphs - GraphSummit Paris
Neo4j - Product Vision and Knowledge Graphs - GraphSummit Paris
 
Neo4j - Product Vision and Knowledge Graphs - GraphSummit Paris
Neo4j - Product Vision and Knowledge Graphs - GraphSummit ParisNeo4j - Product Vision and Knowledge Graphs - GraphSummit Paris
Neo4j - Product Vision and Knowledge Graphs - GraphSummit Paris
 
LORRAINE ANDREI_LEQUIGAN_HOW TO USE WHATSAPP.pptx
LORRAINE ANDREI_LEQUIGAN_HOW TO USE WHATSAPP.pptxLORRAINE ANDREI_LEQUIGAN_HOW TO USE WHATSAPP.pptx
LORRAINE ANDREI_LEQUIGAN_HOW TO USE WHATSAPP.pptx
 

Modern C++ Explained: Move Semantics (Feb 2018)

  • 1. Modern C++ Explained : Move Semantics February 5, 2018 by Olve Maudal
  • 2. In this session I will try to explain rvalue references (aka refref) and move semantics. Here is a code snippet to get us started…
  • 3. #include <iostream> #include <string> 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…
  • 4. #include <iostream> #include <string> 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…
  • 5. #include <iostream> #include <string> 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…
  • 6. #include <iostream> #include <string> 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.
  • 7. #include <iostream> #include <string> void analyze(std::string seq) { std::cout << seq << std::endl; // ... } int main() { std::string seq = "ACTTCTGTATTGGGTCTTTAATAG"; analyze(seq); }
  • 8. #include <iostream> #include <string> void analyze(std::string seq) { std::cout << seq << std::endl; // ... } int main() { std::string seq = "ACTTCTGTATTGGGTCTTTAATAG"; analyze(seq); }
  • 9. #include <iostream> #include <string> void analyze(const std::string & seq) { std::cout << seq << std::endl; // ... } int main() { std::string seq = "ACTTCTGTATTGGGTCTTTAATAG"; analyze(seq); }
  • 10. #include <iostream> #include <string> void analyze(const std::string & seq) { std::cout << seq << std::endl; // ... } int main() { std::string seq = "ACTTCTGTATTGGGTCTTTAATAG"; analyze(seq); }
  • 11. #include <iostream> #include <string> 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…
  • 12. #include <iostream> #include <string> void analyze(const std::string & seq) { std::cout << seq << std::endl; // ... } int main() { std::string seq = "ACTTCTGTATTGGGTCTTTAATAG"; analyze(seq); }
  • 13. #include <iostream> #include <string> void analyze(const std::string & seq) { std::cout << seq << std::endl; // ... } int main() { std::string seq = "ACTTCTGTATTGGGTCTTTAATAG"; analyze(seq); }
  • 14. #include <iostream> #include <string> 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); }
  • 15. #include <iostream> #include <string> 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 $
  • 16. #include <iostream> #include <string> 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 $
  • 17. #include <iostream> #include <string> 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?
  • 18. #include <iostream> #include <string> 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?
  • 19. #include <iostream> #include <string> 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); }
  • 20. #include <iostream> #include <string> 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); }
  • 21. #include <iostream> #include <string> 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); }
  • 22. #include <iostream> #include <string> 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); }
  • 23. #include <iostream> #include <string> 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); }
  • 24. #include <iostream> #include <string> 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); }
  • 25. #include <iostream> #include <string> 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); }
  • 26. #include <iostream> #include <string> 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); }
  • 27. #include <iostream> #include <string> 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); }
  • 28. #include <iostream> #include <string> 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.
  • 29. #include <iostream> #include <string> 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…
  • 30. #include <iostream> #include <string> 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); }
  • 31. #include <iostream> #include <string> 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); }
  • 32. #include <iostream> #include <string> 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); }
  • 33. #include <iostream> #include <string> 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); }
  • 34. #include <iostream> #include <string> 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); }
  • 35. #include <iostream> #include <string> 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 $
  • 36. #include <iostream> #include <string> 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); }
  • 37. #include <iostream> #include <string> 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); }
  • 38. #include <iostream> #include <string> 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()); }
  • 39. #include <iostream> #include <string> 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()); }
  • 40. #include <iostream> #include <string> 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.
  • 41. #include <iostream> #include <string> 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.
  • 42. #include <iostream> #include <string> 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.
  • 43. #include <iostream> #include <string> 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
  • 44. #include <iostream> #include <string> 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?
  • 45. #include <iostream> #include <string> 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
  • 46. #include <iostream> #include <string> 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()); }
  • 47. #include <iostream> #include <string> 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()); }
  • 48. #include <iostream> #include <string> 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()); }
  • 49. #include <iostream> #include <string> 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()); }
  • 50. #include <iostream> #include <string> 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 $
  • 51. #include <iostream> #include <string> 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.
  • 52. #include <iostream> #include <string> 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?
  • 53. #include <iostream> #include <string> 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?
  • 54. #include <iostream> #include <string> 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?
  • 55. #include <iostream> #include <string> 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()); }
  • 56. #include <iostream> #include <string> 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()); }
  • 57. #include <iostream> #include <string> 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); }
  • 58. #include <iostream> #include <string> 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 &&’ $
  • 59. #include <iostream> #include <string> 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.
  • 60. #include <iostream> #include <string> 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); }
  • 61. #include <iostream> #include <string> 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()
  • 62. #include <iostream> #include <string> 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()
  • 63. #include <iostream> #include <string> 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()
  • 64. #include <iostream> #include <string> 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)); }
  • 65. #include <iostream> #include <string> 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 $
  • 66. #include <iostream> #include <string> 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.”
  • 67. #include <iostream> #include <string> 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)
  • 68. #include <iostream> #include <string> 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)
  • 69. #include <iostream> #include <string> 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)); }
  • 70. #include <iostream> #include <string> 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)); }
  • 71. #include <iostream> #include <string> 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)); }
  • 72. #include <iostream> #include <string> 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)); }
  • 73. #include <iostream> #include <string> 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)); }
  • 74. #include <iostream> #include <string> 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)); }
  • 75. #include <iostream> #include <string> 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.
  • 76. #include <iostream> #include <string> 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).
  • 77. #include <iostream> #include <string> 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)); }
  • 78. #include <iostream> #include <string> 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)); }
  • 79. #include <iostream> #include <algorithm> #include <cstring> #include <iterator> 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<char>(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)); }
  • 80. #include <iostream> #include <algorithm> #include <cstring> #include <iterator> 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<char>(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)); }
  • 81. #include <algorithm> #include <cstring> #include <iostream> #include <iterator> 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<char>(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)); }
  • 82. #include <algorithm> #include <cstring> #include <iostream> #include <iterator> 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<char>(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)); }
  • 83. #include <algorithm> #include <cstring> #include <iostream> #include <iterator> 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<char>(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 $
  • 84. #include <algorithm> #include <cstring> #include <iostream> #include <iterator> 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<char>(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.
  • 85. #include <algorithm> #include <cstring> #include <iostream> #include <iterator> 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<char>(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.
  • 86. #include <algorithm> #include <cstring> #include <iostream> #include <iterator> 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<char>(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.
  • 87. #include <algorithm> #include <cstring> #include <iostream> #include <iterator> 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<char>(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)); }
  • 88. #include <algorithm> #include <cstring> #include <iostream> #include <iterator> 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<char>(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 $
  • 89. #include <algorithm> #include <cstring> #include <iostream> #include <iterator> 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<char>(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.
  • 90. #include <algorithm> #include <cstring> #include <iostream> #include <iterator> 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<char>(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.
  • 91. #include <algorithm> #include <cstring> #include <iostream> #include <iterator> 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<char>(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)); }
  • 92. #include <algorithm> #include <cstring> #include <iostream> #include <iterator> 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<char>(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)); }
  • 93. #include <algorithm> #include <cstring> #include <iostream> #include <iterator> 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<char>(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
  • 94. #include <algorithm> #include <cstring> #include <iostream> #include <iterator> 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<char>(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
  • 95. #include <algorithm> #include <cstring> #include <iostream> #include <iterator> 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<char>(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)); }
  • 96. #include <algorithm> #include <cstring> #include <iostream> #include <iterator> 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<char>(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.
  • 97. 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<char>(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... }
  • 98. 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<char>(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... }
  • 99. 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<char>(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... }
  • 100. 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<char>(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... }
  • 101. 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<char>(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... }
  • 102. 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<char>(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... }
  • 103. 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<char>(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... }
  • 104. 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<char>(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 $
  • 105. 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<char>(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.
  • 106. 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<char>(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.
  • 107. 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<char>(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... }
  • 108. 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<char>(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 $
  • 109. 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<char>(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... }
  • 110. 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<char>(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…
  • 111. 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<char>(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…
  • 112. 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<char>(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…
  • 113. 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<char>(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(seq); // ... }
  • 114. 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<char>(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 $
  • 115. 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<char>(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.
  • 116. 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<char>(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.
  • 117. 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<char>(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(seq); // ... }
  • 118. 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<char>(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(seq); // ... }
  • 119. 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<char>(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(seq); // ... }
  • 120. 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<char>(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 $
  • 121. 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<char>(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(seq); // ... }
  • 122. 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<char>(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...
  • 123. 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<char>(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...
  • 124. 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<char>(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...
  • 125. 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<char>(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(seq); seq = tmp; // ... }
  • 126. 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<char>(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 $
  • 127. 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<char>(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.
  • 128. 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<char>(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.
  • 129. 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<char>(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(seq); seq = tmp; // ... }
  • 130. 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<char>(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 $
  • 131. 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<char>(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(seq); seq = tmp; // ... }
  • 132. 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<char>(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.
  • 133. 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<char>(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.
  • 134. 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<char>(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.
  • 135. 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<char>(out)); return out; } std::size_t size; int * data; }; void analyze(Contig && seq) { std::cout << seq << std::endl; // ... Contig tmp(std::move(seq)); // ... }
  • 136. 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<char>(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 $