SlideShare a Scribd company logo
1 of 257
Download to read offline
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

Py.test
Py.testPy.test
Py.testsoasme
 
C++ Programming Course
C++ Programming CourseC++ Programming Course
C++ Programming CourseDennis Chang
 
組み込みでこそC++を使う10の理由
組み込みでこそC++を使う10の理由組み込みでこそC++を使う10の理由
組み込みでこそC++を使う10の理由kikairoya
 
Module 05 Preprocessor and Macros in C
Module 05 Preprocessor and Macros in CModule 05 Preprocessor and Macros in C
Module 05 Preprocessor and Macros in CTushar B Kute
 
CPP Language Basics - Reference
CPP Language Basics - ReferenceCPP Language Basics - Reference
CPP Language Basics - ReferenceMohammed Sikander
 
JavaScript Execution Context
JavaScript Execution ContextJavaScript Execution Context
JavaScript Execution ContextJuan Medina
 
Introduzione ad angular 7/8
Introduzione ad angular 7/8Introduzione ad angular 7/8
Introduzione ad angular 7/8Valerio Radice
 
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
 
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patternsJava 8-streams-collectors-patterns
Java 8-streams-collectors-patternsJosé Paumard
 
Concurrency in Golang
Concurrency in GolangConcurrency in Golang
Concurrency in GolangOliver N
 
C++ 11 Features
C++ 11 FeaturesC++ 11 Features
C++ 11 FeaturesJan Rüegg
 
C#coding guideline その2_20130325
C#coding guideline その2_20130325C#coding guideline その2_20130325
C#coding guideline その2_20130325Yoshihisa Ozaki
 

What's hot (20)

Py.test
Py.testPy.test
Py.test
 
C++ Programming Course
C++ Programming CourseC++ Programming Course
C++ Programming Course
 
組み込みでこそC++を使う10の理由
組み込みでこそC++を使う10の理由組み込みでこそC++を使う10の理由
組み込みでこそC++を使う10の理由
 
C++
C++C++
C++
 
Module 05 Preprocessor and Macros in C
Module 05 Preprocessor and Macros in CModule 05 Preprocessor and Macros in C
Module 05 Preprocessor and Macros in C
 
CPP Language Basics - Reference
CPP Language Basics - ReferenceCPP Language Basics - Reference
CPP Language Basics - Reference
 
JavaScript Execution Context
JavaScript Execution ContextJavaScript Execution Context
JavaScript Execution Context
 
Introduzione ad angular 7/8
Introduzione ad angular 7/8Introduzione ad angular 7/8
Introduzione ad angular 7/8
 
Rust Primer
Rust PrimerRust Primer
Rust Primer
 
Rust-lang
Rust-langRust-lang
Rust-lang
 
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 ...
 
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patternsJava 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
 
Concurrency in Golang
Concurrency in GolangConcurrency in Golang
Concurrency in Golang
 
Rust vs C++
Rust vs C++Rust vs C++
Rust vs C++
 
CSV Files-1.pdf
CSV Files-1.pdfCSV Files-1.pdf
CSV Files-1.pdf
 
OOP C++
OOP C++OOP C++
OOP C++
 
Python basics
Python basicsPython basics
Python basics
 
C++ 11 Features
C++ 11 FeaturesC++ 11 Features
C++ 11 Features
 
C#coding guideline その2_20130325
C#coding guideline その2_20130325C#coding guideline その2_20130325
C#coding guideline その2_20130325
 
Cheat Sheet java
Cheat Sheet javaCheat Sheet java
Cheat Sheet java
 

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 .pdfbermanbeancolungak45
 
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.docxbraycarissa250
 
Javascript built in String Functions
Javascript built in String FunctionsJavascript built in String Functions
Javascript built in String FunctionsAvanitrambadiya
 
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.pdfkostikjaylonshaewe47
 
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.docxadampcarr67227
 
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.17LogeekNightUkraine
 
(Slightly) Smarter Smart Pointers
(Slightly) Smarter Smart Pointers(Slightly) Smarter Smart Pointers
(Slightly) Smarter Smart PointersCarlo 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 overloadingkinan 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 .docxMARRY7
 
array, function, pointer, pattern matching
array, function, pointer, pattern matchingarray, function, pointer, pattern matching
array, function, pointer, pattern matchingShakila Mahjabin
 
Spark with Elasticsearch - umd version 2014
Spark with Elasticsearch - umd version 2014Spark with Elasticsearch - umd version 2014
Spark with Elasticsearch - umd version 2014Holden Karau
 
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.pdffsenterprises
 
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 REDDYMalikireddy 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) Datagreenwop
 

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

Sinoville Clinic ](+27832195400*)[🏥Abortion Pill Prices Sinoville ● Women's A...
Sinoville Clinic ](+27832195400*)[🏥Abortion Pill Prices Sinoville ● Women's A...Sinoville Clinic ](+27832195400*)[🏥Abortion Pill Prices Sinoville ● Women's A...
Sinoville Clinic ](+27832195400*)[🏥Abortion Pill Prices Sinoville ● Women's A...Abortion Clinic
 
architecting-ai-in-the-enterprise-apis-and-applications.pdf
architecting-ai-in-the-enterprise-apis-and-applications.pdfarchitecting-ai-in-the-enterprise-apis-and-applications.pdf
architecting-ai-in-the-enterprise-apis-and-applications.pdfWSO2
 
Effective Strategies for Wix's Scaling challenges - GeeCon
Effective Strategies for Wix's Scaling challenges - GeeConEffective Strategies for Wix's Scaling challenges - GeeCon
Effective Strategies for Wix's Scaling challenges - GeeConNatan Silnitsky
 
Transformer Neural Network Use Cases with Links
Transformer Neural Network Use Cases with LinksTransformer Neural Network Use Cases with Links
Transformer Neural Network Use Cases with LinksJinanKordab
 
Salesforce Introduced Zero Copy Partner Network to Simplify the Process of In...
Salesforce Introduced Zero Copy Partner Network to Simplify the Process of In...Salesforce Introduced Zero Copy Partner Network to Simplify the Process of In...
Salesforce Introduced Zero Copy Partner Network to Simplify the Process of In...CloudMetic
 
The Evolution of Web App Testing_ An Ultimate Guide to Future Trends.pdf
The Evolution of Web App Testing_ An Ultimate Guide to Future Trends.pdfThe Evolution of Web App Testing_ An Ultimate Guide to Future Trends.pdf
The Evolution of Web App Testing_ An Ultimate Guide to Future Trends.pdfkalichargn70th171
 
Software Engineering - Introduction + Process Models + Requirements Engineering
Software Engineering - Introduction + Process Models + Requirements EngineeringSoftware Engineering - Introduction + Process Models + Requirements Engineering
Software Engineering - Introduction + Process Models + Requirements EngineeringPrakhyath Rai
 
^Clinic ^%[+27788225528*Abortion Pills For Sale In harare
^Clinic ^%[+27788225528*Abortion Pills For Sale In harare^Clinic ^%[+27788225528*Abortion Pills For Sale In harare
^Clinic ^%[+27788225528*Abortion Pills For Sale In hararekasambamuno
 
StrimziCon 2024 - Transition to Apache Kafka on Kubernetes with Strimzi.pdf
StrimziCon 2024 - Transition to Apache Kafka on Kubernetes with Strimzi.pdfStrimziCon 2024 - Transition to Apache Kafka on Kubernetes with Strimzi.pdf
StrimziCon 2024 - Transition to Apache Kafka on Kubernetes with Strimzi.pdfsteffenkarlsson2
 
Optimizing Operations by Aligning Resources with Strategic Objectives Using O...
Optimizing Operations by Aligning Resources with Strategic Objectives Using O...Optimizing Operations by Aligning Resources with Strategic Objectives Using O...
Optimizing Operations by Aligning Resources with Strategic Objectives Using O...OnePlan Solutions
 
OpenChain @ LF Japan Executive Briefing - May 2024
OpenChain @ LF Japan Executive Briefing - May 2024OpenChain @ LF Japan Executive Briefing - May 2024
OpenChain @ LF Japan Executive Briefing - May 2024Shane Coughlan
 
Jax, FL Admin Community Group 05.14.2024 Combined Deck
Jax, FL Admin Community Group 05.14.2024 Combined DeckJax, FL Admin Community Group 05.14.2024 Combined Deck
Jax, FL Admin Community Group 05.14.2024 Combined DeckMarc Lester
 
Workshop: Enabling GenAI Breakthroughs with Knowledge Graphs - GraphSummit Milan
Workshop: Enabling GenAI Breakthroughs with Knowledge Graphs - GraphSummit MilanWorkshop: Enabling GenAI Breakthroughs with Knowledge Graphs - GraphSummit Milan
Workshop: Enabling GenAI Breakthroughs with Knowledge Graphs - GraphSummit MilanNeo4j
 
From Theory to Practice: Utilizing SpiraPlan's REST API
From Theory to Practice: Utilizing SpiraPlan's REST APIFrom Theory to Practice: Utilizing SpiraPlan's REST API
From Theory to Practice: Utilizing SpiraPlan's REST APIInflectra
 
How to install and activate eGrabber JobGrabber
How to install and activate eGrabber JobGrabberHow to install and activate eGrabber JobGrabber
How to install and activate eGrabber JobGrabbereGrabber
 
Evolving Data Governance for the Real-time Streaming and AI Era
Evolving Data Governance for the Real-time Streaming and AI EraEvolving Data Governance for the Real-time Streaming and AI Era
Evolving Data Governance for the Real-time Streaming and AI Eraconfluent
 
Auto Affiliate AI Earns First Commission in 3 Hours..pdf
Auto Affiliate  AI Earns First Commission in 3 Hours..pdfAuto Affiliate  AI Earns First Commission in 3 Hours..pdf
Auto Affiliate AI Earns First Commission in 3 Hours..pdfSelfMade bd
 

Recently uploaded (20)

Sinoville Clinic ](+27832195400*)[🏥Abortion Pill Prices Sinoville ● Women's A...
Sinoville Clinic ](+27832195400*)[🏥Abortion Pill Prices Sinoville ● Women's A...Sinoville Clinic ](+27832195400*)[🏥Abortion Pill Prices Sinoville ● Women's A...
Sinoville Clinic ](+27832195400*)[🏥Abortion Pill Prices Sinoville ● Women's A...
 
architecting-ai-in-the-enterprise-apis-and-applications.pdf
architecting-ai-in-the-enterprise-apis-and-applications.pdfarchitecting-ai-in-the-enterprise-apis-and-applications.pdf
architecting-ai-in-the-enterprise-apis-and-applications.pdf
 
Effective Strategies for Wix's Scaling challenges - GeeCon
Effective Strategies for Wix's Scaling challenges - GeeConEffective Strategies for Wix's Scaling challenges - GeeCon
Effective Strategies for Wix's Scaling challenges - GeeCon
 
Abortion Clinic In Johannesburg ](+27832195400*)[ 🏥 Safe Abortion Pills in Jo...
Abortion Clinic In Johannesburg ](+27832195400*)[ 🏥 Safe Abortion Pills in Jo...Abortion Clinic In Johannesburg ](+27832195400*)[ 🏥 Safe Abortion Pills in Jo...
Abortion Clinic In Johannesburg ](+27832195400*)[ 🏥 Safe Abortion Pills in Jo...
 
Transformer Neural Network Use Cases with Links
Transformer Neural Network Use Cases with LinksTransformer Neural Network Use Cases with Links
Transformer Neural Network Use Cases with Links
 
Salesforce Introduced Zero Copy Partner Network to Simplify the Process of In...
Salesforce Introduced Zero Copy Partner Network to Simplify the Process of In...Salesforce Introduced Zero Copy Partner Network to Simplify the Process of In...
Salesforce Introduced Zero Copy Partner Network to Simplify the Process of In...
 
The Evolution of Web App Testing_ An Ultimate Guide to Future Trends.pdf
The Evolution of Web App Testing_ An Ultimate Guide to Future Trends.pdfThe Evolution of Web App Testing_ An Ultimate Guide to Future Trends.pdf
The Evolution of Web App Testing_ An Ultimate Guide to Future Trends.pdf
 
Software Engineering - Introduction + Process Models + Requirements Engineering
Software Engineering - Introduction + Process Models + Requirements EngineeringSoftware Engineering - Introduction + Process Models + Requirements Engineering
Software Engineering - Introduction + Process Models + Requirements Engineering
 
^Clinic ^%[+27788225528*Abortion Pills For Sale In harare
^Clinic ^%[+27788225528*Abortion Pills For Sale In harare^Clinic ^%[+27788225528*Abortion Pills For Sale In harare
^Clinic ^%[+27788225528*Abortion Pills For Sale In harare
 
StrimziCon 2024 - Transition to Apache Kafka on Kubernetes with Strimzi.pdf
StrimziCon 2024 - Transition to Apache Kafka on Kubernetes with Strimzi.pdfStrimziCon 2024 - Transition to Apache Kafka on Kubernetes with Strimzi.pdf
StrimziCon 2024 - Transition to Apache Kafka on Kubernetes with Strimzi.pdf
 
Optimizing Operations by Aligning Resources with Strategic Objectives Using O...
Optimizing Operations by Aligning Resources with Strategic Objectives Using O...Optimizing Operations by Aligning Resources with Strategic Objectives Using O...
Optimizing Operations by Aligning Resources with Strategic Objectives Using O...
 
OpenChain @ LF Japan Executive Briefing - May 2024
OpenChain @ LF Japan Executive Briefing - May 2024OpenChain @ LF Japan Executive Briefing - May 2024
OpenChain @ LF Japan Executive Briefing - May 2024
 
Jax, FL Admin Community Group 05.14.2024 Combined Deck
Jax, FL Admin Community Group 05.14.2024 Combined DeckJax, FL Admin Community Group 05.14.2024 Combined Deck
Jax, FL Admin Community Group 05.14.2024 Combined Deck
 
Workshop: Enabling GenAI Breakthroughs with Knowledge Graphs - GraphSummit Milan
Workshop: Enabling GenAI Breakthroughs with Knowledge Graphs - GraphSummit MilanWorkshop: Enabling GenAI Breakthroughs with Knowledge Graphs - GraphSummit Milan
Workshop: Enabling GenAI Breakthroughs with Knowledge Graphs - GraphSummit Milan
 
From Theory to Practice: Utilizing SpiraPlan's REST API
From Theory to Practice: Utilizing SpiraPlan's REST APIFrom Theory to Practice: Utilizing SpiraPlan's REST API
From Theory to Practice: Utilizing SpiraPlan's REST API
 
How to install and activate eGrabber JobGrabber
How to install and activate eGrabber JobGrabberHow to install and activate eGrabber JobGrabber
How to install and activate eGrabber JobGrabber
 
Abortion Clinic In Springs ](+27832195400*)[ 🏥 Safe Abortion Pills in Springs...
Abortion Clinic In Springs ](+27832195400*)[ 🏥 Safe Abortion Pills in Springs...Abortion Clinic In Springs ](+27832195400*)[ 🏥 Safe Abortion Pills in Springs...
Abortion Clinic In Springs ](+27832195400*)[ 🏥 Safe Abortion Pills in Springs...
 
Evolving Data Governance for the Real-time Streaming and AI Era
Evolving Data Governance for the Real-time Streaming and AI EraEvolving Data Governance for the Real-time Streaming and AI Era
Evolving Data Governance for the Real-time Streaming and AI Era
 
Auto Affiliate AI Earns First Commission in 3 Hours..pdf
Auto Affiliate  AI Earns First Commission in 3 Hours..pdfAuto Affiliate  AI Earns First Commission in 3 Hours..pdf
Auto Affiliate AI Earns First Commission in 3 Hours..pdf
 
Abortion Clinic In Polokwane ](+27832195400*)[ 🏥 Safe Abortion Pills in Polok...
Abortion Clinic In Polokwane ](+27832195400*)[ 🏥 Safe Abortion Pills in Polok...Abortion Clinic In Polokwane ](+27832195400*)[ 🏥 Safe Abortion Pills in Polok...
Abortion Clinic In Polokwane ](+27832195400*)[ 🏥 Safe Abortion Pills in Polok...
 

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 $