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

OOP in C++
OOP in C++OOP in C++
OOP in C++ppd1961
 
C++: Constructor, Copy Constructor and Assignment operator
C++: Constructor, Copy Constructor and Assignment operatorC++: Constructor, Copy Constructor and Assignment operator
C++: Constructor, Copy Constructor and Assignment operatorJussi Pohjolainen
 
Malloc() and calloc() in c
Malloc() and calloc() in cMalloc() and calloc() in c
Malloc() and calloc() in cMahesh Tibrewal
 
Basic i/o & file handling in java
Basic i/o & file handling in javaBasic i/o & file handling in java
Basic i/o & file handling in javaJayasankarPR2
 
Top C Language Interview Questions and Answer
Top C Language Interview Questions and AnswerTop C Language Interview Questions and Answer
Top C Language Interview Questions and AnswerVineet Kumar Saini
 
API Asynchrones en Java 8
API Asynchrones en Java 8API Asynchrones en Java 8
API Asynchrones en Java 8José Paumard
 
Templates in C++
Templates in C++Templates in C++
Templates in C++Tech_MX
 
The Rust Programming Language: an Overview
The Rust Programming Language: an OverviewThe Rust Programming Language: an Overview
The Rust Programming Language: an OverviewRoberto Casadei
 
Rust Programming Language
Rust Programming LanguageRust Programming Language
Rust Programming LanguageJaeju Kim
 
Deep drive into rust programming language
Deep drive into rust programming languageDeep drive into rust programming language
Deep drive into rust programming languageVigneshwer Dhinakaran
 
Rust: Unlocking Systems Programming
Rust: Unlocking Systems ProgrammingRust: Unlocking Systems Programming
Rust: Unlocking Systems ProgrammingC4Media
 
Introduction to cpp
Introduction to cppIntroduction to cpp
Introduction to cppNilesh Dalvi
 

What's hot (20)

C++ Programming
C++ ProgrammingC++ Programming
C++ Programming
 
OOP in C++
OOP in C++OOP in C++
OOP in C++
 
Preprocessors
Preprocessors Preprocessors
Preprocessors
 
C++: Constructor, Copy Constructor and Assignment operator
C++: Constructor, Copy Constructor and Assignment operatorC++: Constructor, Copy Constructor and Assignment operator
C++: Constructor, Copy Constructor and Assignment operator
 
Why rust?
Why rust?Why rust?
Why rust?
 
Functions in c++
Functions in c++Functions in c++
Functions in c++
 
Malloc() and calloc() in c
Malloc() and calloc() in cMalloc() and calloc() in c
Malloc() and calloc() in c
 
Basic i/o & file handling in java
Basic i/o & file handling in javaBasic i/o & file handling in java
Basic i/o & file handling in java
 
Top C Language Interview Questions and Answer
Top C Language Interview Questions and AnswerTop C Language Interview Questions and Answer
Top C Language Interview Questions and Answer
 
Unary operator overloading
Unary operator overloadingUnary operator overloading
Unary operator overloading
 
Rust vs C++
Rust vs C++Rust vs C++
Rust vs C++
 
Complete Java Course
Complete Java CourseComplete Java Course
Complete Java Course
 
API Asynchrones en Java 8
API Asynchrones en Java 8API Asynchrones en Java 8
API Asynchrones en Java 8
 
C++
C++C++
C++
 
Templates in C++
Templates in C++Templates in C++
Templates in C++
 
The Rust Programming Language: an Overview
The Rust Programming Language: an OverviewThe Rust Programming Language: an Overview
The Rust Programming Language: an Overview
 
Rust Programming Language
Rust Programming LanguageRust Programming Language
Rust Programming Language
 
Deep drive into rust programming language
Deep drive into rust programming languageDeep drive into rust programming language
Deep drive into rust programming language
 
Rust: Unlocking Systems Programming
Rust: Unlocking Systems ProgrammingRust: Unlocking Systems Programming
Rust: Unlocking Systems Programming
 
Introduction to cpp
Introduction to cppIntroduction to cpp
Introduction to cpp
 

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

Introduction Computer Science - Software Design.pdf
Introduction Computer Science - Software Design.pdfIntroduction Computer Science - Software Design.pdf
Introduction Computer Science - Software Design.pdfFerryKemperman
 
Cloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackCloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackVICTOR MAESTRE RAMIREZ
 
英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作qr0udbr0
 
Implementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureImplementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureDinusha Kumarasiri
 
Xen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdfXen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdfStefano Stabellini
 
Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024Andreas Granig
 
EY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityEY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityNeo4j
 
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...soniya singh
 
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...Matt Ray
 
What is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWhat is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWave PLM
 
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptxKnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptxTier1 app
 
Building Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop SlideBuilding Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop SlideChristina Lin
 
Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)OPEN KNOWLEDGE GmbH
 
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024StefanoLambiase
 
How to Track Employee Performance A Comprehensive Guide.pdf
How to Track Employee Performance A Comprehensive Guide.pdfHow to Track Employee Performance A Comprehensive Guide.pdf
How to Track Employee Performance A Comprehensive Guide.pdfLivetecs LLC
 
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...OnePlan Solutions
 
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed DataAlluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed DataAlluxio, Inc.
 
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEBATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEOrtus Solutions, Corp
 
Intelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmIntelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmSujith Sukumaran
 

Recently uploaded (20)

Introduction Computer Science - Software Design.pdf
Introduction Computer Science - Software Design.pdfIntroduction Computer Science - Software Design.pdf
Introduction Computer Science - Software Design.pdf
 
Cloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackCloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStack
 
英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作
 
Implementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureImplementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with Azure
 
Xen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdfXen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdf
 
Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024
 
EY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityEY_Graph Database Powered Sustainability
EY_Graph Database Powered Sustainability
 
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
 
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
 
What is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWhat is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need It
 
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptxKnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
 
Building Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop SlideBuilding Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
 
Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)
 
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
 
How to Track Employee Performance A Comprehensive Guide.pdf
How to Track Employee Performance A Comprehensive Guide.pdfHow to Track Employee Performance A Comprehensive Guide.pdf
How to Track Employee Performance A Comprehensive Guide.pdf
 
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
 
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed DataAlluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
 
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEBATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
 
2.pdf Ejercicios de programación competitiva
2.pdf Ejercicios de programación competitiva2.pdf Ejercicios de programación competitiva
2.pdf Ejercicios de programación competitiva
 
Intelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmIntelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalm
 

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 $