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

TDD in C - Recently Used List Kata
TDD in C - Recently Used List KataTDD in C - Recently Used List Kata
TDD in C - Recently Used List KataOlve Maudal
 
Understand more about C
Understand more about CUnderstand more about C
Understand more about CYi-Hsiu Hsu
 
JavaScript Conditional Statements
JavaScript Conditional StatementsJavaScript Conditional Statements
JavaScript Conditional StatementsMarlon Jamera
 
Introduction to cpp
Introduction to cppIntroduction to cpp
Introduction to cppNilesh Dalvi
 
C++ Programming Course
C++ Programming CourseC++ Programming Course
C++ Programming CourseDennis Chang
 
Oops practical file
Oops practical fileOops practical file
Oops practical fileAnkit Dixit
 
Friend functions
Friend functions Friend functions
Friend functions Megha Singh
 
php and sapi and zendengine2 and...
php and sapi and zendengine2 and...php and sapi and zendengine2 and...
php and sapi and zendengine2 and...do_aki
 
Let us c(by yashwant kanetkar) chapter 2 solution
Let us c(by yashwant kanetkar) chapter 2 solutionLet us c(by yashwant kanetkar) chapter 2 solution
Let us c(by yashwant kanetkar) chapter 2 solutionrohit kumar
 
Let us c (5th and 12th edition by YASHVANT KANETKAR) chapter 2 solution
Let us c (5th and 12th edition by YASHVANT KANETKAR) chapter 2 solutionLet us c (5th and 12th edition by YASHVANT KANETKAR) chapter 2 solution
Let us c (5th and 12th edition by YASHVANT KANETKAR) chapter 2 solutionHazrat Bilal
 
C multiple choice questions and answers pdf
C multiple choice questions and answers pdfC multiple choice questions and answers pdf
C multiple choice questions and answers pdfchoconyeuquy
 
Let us C (by yashvant Kanetkar) chapter 3 Solution
Let us C   (by yashvant Kanetkar) chapter 3 SolutionLet us C   (by yashvant Kanetkar) chapter 3 Solution
Let us C (by yashvant Kanetkar) chapter 3 SolutionHazrat Bilal
 
OOP in C++
OOP in C++OOP in C++
OOP in C++ppd1961
 
Introduction to Rust language programming
Introduction to Rust language programmingIntroduction to Rust language programming
Introduction to Rust language programmingRodolfo Finochietti
 
Let us c(by Yashwant Kanetkar) 5th edition solution chapter 1
Let us c(by Yashwant Kanetkar) 5th edition solution chapter 1Let us c(by Yashwant Kanetkar) 5th edition solution chapter 1
Let us c(by Yashwant Kanetkar) 5th edition solution chapter 1rohit kumar
 

What's hot (20)

TDD in C - Recently Used List Kata
TDD in C - Recently Used List KataTDD in C - Recently Used List Kata
TDD in C - Recently Used List Kata
 
C++ file
C++ fileC++ file
C++ file
 
Understand more about C
Understand more about CUnderstand more about C
Understand more about C
 
JavaScript Conditional Statements
JavaScript Conditional StatementsJavaScript Conditional Statements
JavaScript Conditional Statements
 
Deep C
Deep CDeep C
Deep C
 
Tutorial consumir DLL C++ en C#
Tutorial consumir DLL C++ en C#Tutorial consumir DLL C++ en C#
Tutorial consumir DLL C++ en C#
 
Introduction to cpp
Introduction to cppIntroduction to cpp
Introduction to cpp
 
C++ Programming Course
C++ Programming CourseC++ Programming Course
C++ Programming Course
 
Oops practical file
Oops practical fileOops practical file
Oops practical file
 
Friend functions
Friend functions Friend functions
Friend functions
 
php and sapi and zendengine2 and...
php and sapi and zendengine2 and...php and sapi and zendengine2 and...
php and sapi and zendengine2 and...
 
Let us c(by yashwant kanetkar) chapter 2 solution
Let us c(by yashwant kanetkar) chapter 2 solutionLet us c(by yashwant kanetkar) chapter 2 solution
Let us c(by yashwant kanetkar) chapter 2 solution
 
Constructor and destructor
Constructor  and  destructor Constructor  and  destructor
Constructor and destructor
 
Let us c (5th and 12th edition by YASHVANT KANETKAR) chapter 2 solution
Let us c (5th and 12th edition by YASHVANT KANETKAR) chapter 2 solutionLet us c (5th and 12th edition by YASHVANT KANETKAR) chapter 2 solution
Let us c (5th and 12th edition by YASHVANT KANETKAR) chapter 2 solution
 
C multiple choice questions and answers pdf
C multiple choice questions and answers pdfC multiple choice questions and answers pdf
C multiple choice questions and answers pdf
 
Let us C (by yashvant Kanetkar) chapter 3 Solution
Let us C   (by yashvant Kanetkar) chapter 3 SolutionLet us C   (by yashvant Kanetkar) chapter 3 Solution
Let us C (by yashvant Kanetkar) chapter 3 Solution
 
OOP in C++
OOP in C++OOP in C++
OOP in C++
 
Introduction to Rust language programming
Introduction to Rust language programmingIntroduction to Rust language programming
Introduction to Rust language programming
 
Let us c(by Yashwant Kanetkar) 5th edition solution chapter 1
Let us c(by Yashwant Kanetkar) 5th edition solution chapter 1Let us c(by Yashwant Kanetkar) 5th edition solution chapter 1
Let us c(by Yashwant Kanetkar) 5th edition solution chapter 1
 
Clean code
Clean codeClean code
Clean code
 

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

%in Soweto+277-882-255-28 abortion pills for sale in soweto
%in Soweto+277-882-255-28 abortion pills for sale in soweto%in Soweto+277-882-255-28 abortion pills for sale in soweto
%in Soweto+277-882-255-28 abortion pills for sale in sowetomasabamasaba
 
The title is not connected to what is inside
The title is not connected to what is insideThe title is not connected to what is inside
The title is not connected to what is insideshinachiaurasa2
 
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM TechniquesAI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM TechniquesVictorSzoltysek
 
%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrandmasabamasaba
 
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...WSO2
 
tonesoftg
tonesoftgtonesoftg
tonesoftglanshi9
 
WSO2Con2024 - Enabling Transactional System's Exponential Growth With Simplicity
WSO2Con2024 - Enabling Transactional System's Exponential Growth With SimplicityWSO2Con2024 - Enabling Transactional System's Exponential Growth With Simplicity
WSO2Con2024 - Enabling Transactional System's Exponential Growth With SimplicityWSO2
 
AI & Machine Learning Presentation Template
AI & Machine Learning Presentation TemplateAI & Machine Learning Presentation Template
AI & Machine Learning Presentation TemplatePresentation.STUDIO
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsJhone kinadey
 
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital TransformationWSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital TransformationWSO2
 
WSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go PlatformlessWSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go PlatformlessWSO2
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️Delhi Call girls
 
VTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnVTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnAmarnathKambale
 
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...Jittipong Loespradit
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️Delhi Call girls
 
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...masabamasaba
 
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfonteinmasabamasaba
 
%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Harare%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Hararemasabamasaba
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...Health
 
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...Bert Jan Schrijver
 

Recently uploaded (20)

%in Soweto+277-882-255-28 abortion pills for sale in soweto
%in Soweto+277-882-255-28 abortion pills for sale in soweto%in Soweto+277-882-255-28 abortion pills for sale in soweto
%in Soweto+277-882-255-28 abortion pills for sale in soweto
 
The title is not connected to what is inside
The title is not connected to what is insideThe title is not connected to what is inside
The title is not connected to what is inside
 
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM TechniquesAI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
 
%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand
 
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
 
tonesoftg
tonesoftgtonesoftg
tonesoftg
 
WSO2Con2024 - Enabling Transactional System's Exponential Growth With Simplicity
WSO2Con2024 - Enabling Transactional System's Exponential Growth With SimplicityWSO2Con2024 - Enabling Transactional System's Exponential Growth With Simplicity
WSO2Con2024 - Enabling Transactional System's Exponential Growth With Simplicity
 
AI & Machine Learning Presentation Template
AI & Machine Learning Presentation TemplateAI & Machine Learning Presentation Template
AI & Machine Learning Presentation Template
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial Goals
 
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital TransformationWSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
 
WSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go PlatformlessWSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go Platformless
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
 
VTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnVTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learn
 
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
 
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
 
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
 
%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Harare%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Harare
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
 
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
 

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 $