To better understand copy elision I wrote a test app that did a simple action in copy and move constructors/assignment operators and counted the times it was copied or moved. I noticed however that there was a copy when I created an rvalue and passed it directly rather than creating a lvalue then passing it in.
I'm trying to understand if this is compiler defined or part of the language specification? Additionally I'm trying to understand why it wouldn't be omitted as it seems like it should just be a construction rather than a copy?
struct Bar {
Bar() {}
Bar(const Bar& a)
: cp_count{a.cp_count + 1}, mv_count{a.mv_count} {}
Bar& operator=(const Bar& a) {
cp_count = a.cp_count + 1;
mv_count = a.mv_count;
return *this;
}
Bar(Bar&& a)
: cp_count{a.cp_count}, mv_count{a.mv_count + 1} {}
Bar& operator=(Bar&& a) {
cp_count = a.cp_count;
mv_count = a.mv_count + 1;
return *this;
}
int cp_count = 0;
int mv_count = 0;
};
struct Foo {
Bar bar;
std::function<void(Bar)> setter;
std::function<void(Bar)> setter2;
Foo() {
setter = [this](Bar a) {
bar = a;
};
setter2 = [this](Bar a) {
bar = std::move(a);
};
}
};
int main() {
std::cout << std::endl << "base line" << std::endl;
{
Foo foo;
std::cout << "mv_count = " << foo.bar.mv_count << " cp_count = " << foo.bar.cp_count << std::endl;
}
std::cout << std::endl << "in-place then copy" << std::endl;
{
Foo foo;
foo.setter(Bar{});
std::cout << "mv_count = " << foo.bar.mv_count << " cp_count = " << foo.bar.cp_count << std::endl;
}
std::cout << std::endl << "copy then copy" << std::endl;
{
Foo foo;
Bar bar{};
foo.setter(bar);
std::cout << "mv_count = " << foo.bar.mv_count << " cp_count = " << foo.bar.cp_count << std::endl;
}
std::cout << std::endl << "move then copy" << std::endl;
{
Foo foo;
Bar bar{};
foo.setter(std::move(bar));
std::cout << "mv_count = " << foo.bar.mv_count << " cp_count = " << foo.bar.cp_count << std::endl;
}
std::cout << std::endl << "in-place then move" << std::endl;
{
Foo foo;
foo.setter2(Bar{});
std::cout << "mv_count = " << foo.bar.mv_count << " cp_count = " << foo.bar.cp_count << std::endl;
}
std::cout << std::endl << "copy then move" << std::endl;
{
Foo foo;
Bar bar{};
foo.setter2(bar);
std::cout << "mv_count = " << foo.bar.mv_count << " cp_count = " << foo.bar.cp_count << std::endl;
}
std::cout << std::endl << "move then move" << std::endl;
{
Foo foo;
Bar bar{};
foo.setter2(std::move(bar));
std::cout << "mv_count = " << foo.bar.mv_count << " cp_count = " << foo.bar.cp_count << std::endl;
}
}
The output I received from my testing
base line
mv_count = 0 cp_count = 0
in-place then copy
mv_count = 1 cp_count = 1
copy then copy
mv_count = 1 cp_count = 2
move then copy
mv_count = 2 cp_count = 1
in-place then move
mv_count = 2 cp_count = 0
copy then move
mv_count = 2 cp_count = 1
move then move
mv_count = 3 cp_count = 0