C++ copy and move constructors with lambda expressions - c++

I've bumped into something strange with C++ copy and move constructors, here when passing to the lambda expression both the copy and move constructors get executed. Strangely though, when I change the declared type of the lambda to auto or use the regular_test function, I get the expected behaviour (copy construction only). Does anyone understand why this is? (tested with both clang and gcc, not msvc)
#include <iostream>
#include <iomanip>
#include <functional>
using namespace std;
struct Test {
inline Test() {
cout << setw(20) << "constructor ";
PrintAddress();
}
Test(const Test&) {
cout << setw(20) << "copy constructor ";
PrintAddress();
}
Test& operator=(const Test&) {
cout << setw(20) << "copy assignment ";
PrintAddress();
return *this;
}
Test(Test&& other) {
cout << setw(20) << "move constructor ";
PrintAddress();
}
Test& operator=(Test&&) {
cout << setw(20) << "move assignment ";
PrintAddress();
return *this;
}
virtual ~Test() {
cout << setw(20) << "destructor ";
PrintAddress();
}
void PrintAddress() {
cout << "Test&: " << this << endl;
}
};
Test regular_test (Test t) {
cout << "regular_test" << endl;
return t;
}
int main() {
cout << "start" << endl;
function<Test(Test)> lambda_test = [] (Test t) {
cout << "lambda_test" << endl;
return t;
};
Test t;
lambda_test(t);
//regular_test(t);
cout << "done" << endl;
return 0;
}
start
constructor Test&: 0x7fffef6faf28
copy constructor Test&: 0x7fffef6faf08
move constructor Test&: 0x7fffef6fade8
lambda_test
move constructor Test&: 0x7fffef6faf10
destructor Test&: 0x7fffef6fade8
destructor Test&: 0x7fffef6faf10
destructor Test&: 0x7fffef6faf08
done
destructor Test&: 0x7fffef6faf28

because std::function::operator() is defined based on the template type of class.
R operator()( Args... args ) const; // Args are parameter of the *class*
so std::function<Test(Test)> would have Test operator()(Test), which would make a copy by itself, then forward to lambda_test (a move)

Related

Constructing elements of union

The following class outputs when a constructor is called:
class A {
public:
A() {
std::cout << "Default Constructor called at address:" << this << "!\n";
}
A(int val) : val_(val) {
std::cout << "Secondary Constructor called at address:" << this << "\n";
}
A(A&& other) : val_(std::move(other.val_)) {
std::cout << "Move constructor called from " << &other << " to " << this << "\n";
}
A& operator=(A&& other) {
std::cout << "Move assignment called from " << &other << " to " << this << "\n";
val_ = std::move(other.val_);
return *this;
}
private:
int val_;
};
This union allows me to skip the default constructor of A, which is desirable:
union MyUnion {
MyUnion() {}
A a;
};
My goal with main was to move-construct a. Instead it move-assigns the a.
int main(){
MyUnion my_union[2];
std::cout << "Entering for loop:\n";
for (int i = 0; i < 2; ++i){
my_union[i].a = std::move(A(i));
}
return 0;
}
An example output from the above main function is:
Entering for loop:
Secondary Constructor called at address:0x7ffcdd3553a8
Move assignment called from 0x7ffcdd3553a8 to 0x7ffcdd3553b0
Secondary Constructor called at address:0x7ffcdd3553a8
Move assignment called from 0x7ffcdd3553a8 to 0x7ffcdd3553b4
I wanted to move-construct, but I'm instead move-assigning. Move-assigning without move constructing is unsafe in general. The solution I came up with was to use std::construct_at. I also added a destructor. The full program with these modifications is below:
#include <iostream>
#include <memory>
#include <utility>
class A {
public:
A() {
std::cout << "Default Constructor called at address:" << this << "!\n";
}
A(int val) : val_(val) {
std::cout << "Secondary Constructor called at address:" << this << "\n";
}
A(A&& other) : val_(std::move(other.val_)) {
std::cout << "Move constructor called from " << &other << " to " << this << "\n";
}
A& operator=(A&& other) {
std::cout << "Move assignment called from " << &other << " to " << this << "\n";
val_ = std::move(other.val_);
return *this;
}
~A() {
std::cout << "Destructor called at " << this << "\n";
}
private:
int val_;
};
union MyUnion {
MyUnion() {
std::cout << "MyUnion Constructor called!\n";
}
void ConstructAt(A&& other){
std::construct_at(&a, std::forward<A>(other));
}
A a;
~MyUnion(){
std::cout << "MyUnion Destructor called!\n";
a.~A();
}
};
int main(){
MyUnion my_union[2];
std::cout << "\nEntering for loop:\n";
for (int i = 0; i < 2; ++i){
my_union[i].ConstructAt(std::move(A(i)));
}
std::cout << "\nExiting for loop:\n";
return 0;
}
This gives output:
MyUnion Constructor called!
MyUnion Constructor called!
Entering for loop:
Secondary Constructor called at address:0x7ffe38dd4838
Move constructor called from 0x7ffe38dd4838 to 0x7ffe38dd4840
Destructor called at 0x7ffe38dd4838
Secondary Constructor called at address:0x7ffe38dd4838
Move constructor called from 0x7ffe38dd4838 to 0x7ffe38dd4844
Destructor called at 0x7ffe38dd4838
Exiting for loop:
MyUnion Destructor called!
Destructor called at 0x7ffe38dd4844
MyUnion Destructor called!
Destructor called at 0x7ffe38dd4840
This appears to be what I want.
Question
Was this the correct way to call the move constructor? Am I using std::forward correctly? Is this the correct way to handle the destructor of a union?

Move Constructor invoked for a function, but NRVO expected in C++

I don't understand why a move constructor is being invoked during the main function in the code below, specifically the output is:
FString::FString(string one)
FString::FString(string two)
FString::Move Constructor
FString::Move Assign
COMPLETE
So the line I am concerned about is "FString::Move Constructor" - this implies to me that the move constructor is getting invoked in order to fulfill the return statement of the function GetStringTemp(), but from what I understood NRVO should mean that the move constructor should not be invoked. Am I misunderstanding the behaviour of NVRO? Thanks in advance
#include <iostream>
#include <string>
class FString
{
public:
FString(std::string newstring)
: _string(newstring)
{
std::cout << "FString::FString(string "+newstring+")" << std::endl;
}
FString(const FString& rhs)
{
std::cout << "FString::Copy Constructor" << std::endl;
}
FString(FString&& rhs)
{
std::cout << "FString::Move Constructor" << std::endl;
}
FString& operator=(const FString& rhs)
{
std::cout << "FString::Copy Assign" << std::endl;
return *this;
}
FString& operator=(FString&& rhs)
{
std::cout << "FString::Move Assign" << std::endl;
return *this;
}
void Print()
{
std::cout << "Printing: "+_string << std::endl;
}
private:
std::string _string;
};
FString GetTempString()
{
FString temp = FString("two"); // 2: Expected Constructor cout
return temp; // No expected constructor as NVRO assumed
}
int main()
{
FString myString = FString("one"); // 1: Expected Constructor cout
myString = GetTempString(); // 3: Expected Move Assignment cout
std::cout << "COMPLETE" << std::endl;
}

Pointer member is not initialized in copy constructor

In my application
#include <iostream>
class TestClassA
{
public:
int* m_ptr;
TestClassA(int a)
{
m_ptr = new int(a);
std::cout << "Constructor. this: " << this << " m_ptr: " << m_ptr << std::endl;
}
TestClassA(const TestClassA& copy)
{
std::cout << "Copy Constructor. copy: " << &copy << " -> this: " << this << std::endl;
std::cout << "Copy Constructor. old this->m_ptr: " << m_ptr << std::endl;
delete m_ptr; // not initialized pointer
m_ptr = new int;
std::cout << "Copy Constructor. new this->m_ptr: " << m_ptr << std::endl;
*m_ptr = *copy.m_ptr;
}
// passing by value, thus a copy constructor calls first
TestClassA& operator=(TestClassA tmp)
{
std::cout << "Copy assignment " << this << " <- " << &tmp << std::endl;
std::swap(m_ptr, tmp.m_ptr);
return *this;
}
~TestClassA()
{
std::cout << "Destructor " << this << std::endl;
delete m_ptr;
m_ptr = nullptr;
}
};
void testAssignment()
{
TestClassA tca1(1);
std::cout << "tca1.m_ptr: " << tca1.m_ptr << std::endl;
TestClassA tca2(2);
std::cout << "tca2.m_ptr: " << tca2.m_ptr << std::endl;
tca2 = tca1;
}
int main()
{
testAssignment();
return 0;
}
When I call assignment operator receiving arguments by value, copy constructor calls. I guess it is to create a temporary variable and to copy the state of tcs1 to it. The issue is that m_ptr member of this temporary is not initialized, so I can't delete previous m_ptr value to write a new one. What is the proper way of implementing copy constructor in this case?
Copy constructor is a constructor, not an assignment operator. The diffetence is precisely the absence of preexisting resources to destroy. You don't need to destroy anything, just initialize.
The copy constructor is called because you did not make it accept a const reference:
TestClassA& operator=(const TestClassA& tmp)
// ^ ^
It is the tmp parameter that is initialized in the example, not the this of the operator.
Of course, you'll need a local variable to get the swap trick to work, but at least it will be explicit in your code.

Why is the constructor/destructor called once? [duplicate]

This question already has answers here:
What are copy elision and return value optimization?
(5 answers)
Closed 8 years ago.
Here is my source code:
#include <iostream>
#include <memory>
#include <vector>
using namespace std;
class Copy {
public:
Copy() {
cout << "Constructor called" << endl;
};
Copy(const Copy& copy) {
cout << "Copy constructor called" << endl;
}
Copy& operator=(Copy copy) {
cout << "Copy-Assign constructor called" << endl;
return *this;
}
Copy(Copy &&copy) noexcept {
cout << "Move constructor called" << endl;
}
Copy& operator=(Copy &&copy) noexcept {
cout << "Move-Assign constructor called" << endl;
return *this;
}
~Copy() {
cout << "Destructor called" << endl;
}
};
Copy TestCopy() {
Copy cop;
return cop;
}
vector<Copy> TestCopyVector() {
vector<Copy> copyVector = vector<Copy>{Copy()};
return copyVector;
}
int main()
{
Copy cop = TestCopy();
//TestCopy();
//vector<Copy> copyVector = TestCopyVector();
return 0;
}
In My understanding the line
Copy cop = TestCopy();
Should call Copy's move-assignment. While the output is like bellow:
$ ./test
Constructor called
Destructor called
Could you anybody help explain this? Thanks.
It's called RVO (Return value optimization) and it's a well know optimization the compiler is allowed to make in similar situations.

Are Rvalue members indeed Rvalues too?

They say that members of Rvalues are also Rvalues - which makes a lot of sense. So this is either a VC++-specific bug or a bug in my understanding of Rvalues.
Take this toy code:
#include <vector>
#include <iostream>
using namespace std;
struct MyTypeInner
{
MyTypeInner() {};
~MyTypeInner() { cout << "mt2 dtor" << endl; }
MyTypeInner(const MyTypeInner& other) { cout << "mt2 copy ctor" << endl; }
MyTypeInner(MyTypeInner&& other) { cout << "mt2 move ctor" << endl; }
const MyTypeInner& operator = (const MyTypeInner& other)
{
cout << "mt2 copy =" << endl; return *this;
}
const MyTypeInner& operator = (MyTypeInner&& other)
{
cout << "mt2 move =" << endl; return *this;
}
};
struct MyTypeOuter
{
MyTypeInner mt2;
MyTypeOuter() {};
~MyTypeOuter() { cout << "mt1 dtor" << endl; }
MyTypeOuter(const MyTypeOuter& other) { cout << "mt1 copy ctor" << endl; mt2 = other.mt2; }
MyTypeOuter(MyTypeOuter&& other) { cout << "mt1 move ctor" << endl; mt2 = other.mt2; }
const MyTypeOuter& operator = (const MyTypeOuter& other)
{
cout << "mt1 copy =" << endl; mt2 = other.mt2; return *this;
}
const MyTypeOuter& operator = (MyTypeOuter&& other)
{
cout << "mt1 move =" << endl; mt2 = other.mt2; return *this;
}
};
MyTypeOuter func() { MyTypeOuter mt; return mt; }
int _tmain()
{
MyTypeOuter mt = func();
return 0;
}
This code outputs:
mt1 move ctor
mt2 copy =
mt1 dtor
mt2 dtor
That is, MyTypeOuter's move ctor calls MyTypeInner's copy, not move. If I modify the code to:
MyTypeOuter(MyTypeOuter&& other)
{ cout << "mt1 move ctor" << endl; mt2 = std::move(other.mt2); }
The output is as expected:
mt1 move ctor
mt2 move =
mt1 dtor
mt2 dtor
It seems VC++ (both 2010 and 2013) do not respect this part of the standard. Or am I missing something?
Whether the member of an rvalue is an rvalue or not is not the issue here, because you are dealing with lvalues inside your assignment operator.
In this move assignment operator,
const MyTypeOuter& operator = (MyTypeOuter&& other)
{
cout << "mt1 move =" << endl;
mt2 = other.mt2;
return *this;
}
other is an lvalue (since it has a name), and by extension so is other.mt2. When you say mt2 = other.mt2 you can only invoke the standard assignment operator.
In order to invoke the move constructor, you need to make other.mt2 look like an rvalue, and this is what std::move achieves:
const MyTypeOuter& operator = (MyTypeOuter&& other)
{
cout << "mt1 move =" << endl;
mt2 = std::move(other.mt2);
return *this;
}
See this related question.