why copy ctor not called

S

subramanian100in

consider the following program:

#include <iostream>

using namespace std;

class Test
{
public:
Test(int xx) : x(xx) { cout << x << endl; }

Test add(const Test & obj);

Test(const Test & obj) : x(obj.x)
{ cout << "copy ctor " << x << endl; }

int x;
};

Test Test::add(const Test & obj)
{
return x + obj.x;
}

int main()
{
Test a(10);
Test b(20);

cout << a.add(b).x << endl;

return 0;
}

This program compiles fine and produces the following output
in both g++ and VC++ Express Edition 2005
10
20
30
30

I thought the copy ctor would be called when we invoke a.add(b)
because Test::add() returns an object of Test type and here
I expected the copy ctor to be called.

I do not understand why the copy ctor is not called.
Is it optimized away ? Or this is the expected behaviour.

Kindly clarify.

Thanks
V.Subramanian
 
D

dolphin

consider the following program:

#include <iostream>

using namespace std;

class Test
{
public:
Test(int xx) : x(xx) { cout << x << endl; }

Test add(const Test & obj);

Test(const Test & obj) : x(obj.x)
{ cout << "copy ctor " << x << endl; }

int x;

};

Test Test::add(const Test & obj)
{
return x + obj.x;

}

int main()
{
Test a(10);
Test b(20);

cout << a.add(b).x << endl;

return 0;

}

This program compiles fine and produces the following output
in both g++ and VC++ Express Edition 2005
10
20
30
30

I thought the copy ctor would be called when we invoke a.add(b)
because Test::add() returns an object of Test type and here
I expected the copy ctor to be called.

I do not understand why the copy ctor is not called.
Is it optimized away ? Or this is the expected behaviour.

Kindly clarify.

Thanks
V.Subramanian

your function add use referenece as argument. It does not create a new
temp object. So it does not call a copy constructor.
 
K

Kira Yamato

consider the following program:

#include <iostream>

using namespace std;

class Test
{
public:
Test(int xx) : x(xx) { cout << x << endl; }

Test add(const Test & obj);

Test(const Test & obj) : x(obj.x)
{ cout << "copy ctor " << x << endl; }

int x;
};

Test Test::add(const Test & obj)
{
return x + obj.x;
}

int main()
{
Test a(10);
Test b(20);

cout << a.add(b).x << endl;

return 0;
}

This program compiles fine and produces the following output
in both g++ and VC++ Express Edition 2005
10
20
30
30

I thought the copy ctor would be called when we invoke a.add(b)
because Test::add() returns an object of Test type and here
I expected the copy ctor to be called.

I do not understand why the copy ctor is not called.
Is it optimized away ? Or this is the expected behaviour.

Hmm... Interesting. Even the following code won't call the copy constructor:

void func()
{
Test a(10);
Test b = a.add(a); // I expected this line to invoke copy
ctor but didn't!
cout << b.x << endl;
}

And I added the destructor too:

~Test() { cout << "destroy " << x << endl; }

My output was this:

10
20
30
30
destroy 30
destroy 20
destroy 10

The behavior seems to be this: When you're copying from a temporary
object, it just does a bitwise copy and does not call the temporary
object's destructor.

FYI, I'm using g++ 4.0.1.
 
K

Kira Yamato

Hmm... Interesting. Even the following code won't call the copy constructor:

void func()
{
Test a(10);
Test b = a.add(a); // I expected this line to invoke copy
ctor but didn't!
cout << b.x << endl;
}

And I added the destructor too:

~Test() { cout << "destroy " << x << endl; }

My output was this:

10
20
30
30
destroy 30
destroy 20
destroy 10

The behavior seems to be this: When you're copying from a temporary
object, it just does a bitwise copy and does not call the temporary
object's destructor.

FYI, I'm using g++ 4.0.1.

Ok, I found this in the manpages for g++:

-fno-elide-constructors
The C++ standard allows an implementation to omit creating a tempo-
rary which is only used to initialize another object of the same
type. Specifying this option disables that optimization, and
forces G++ to call the copy constructor in all cases.

So, the answer is that it is a C++ standard behavior.
 
K

Kaz Kylheku

I thought the copy ctor would be called when we invoke a.add(b)
because Test::add() returns an object of Test type and here
I expected the copy ctor to be called.

A possible implementation strategy is that the calling function
reserves some space for the return object, and gives the function a
pointer to this space.

With these calling conventions, the function can construct the
returned object directly in that space.
I do not understand why the copy ctor is not called.
Is it optimized away ?

The temporary object is optimized away. No temporary object, therefore
no constructor.
 
I

Ian Collins

consider the following program:

#include <iostream>

using namespace std;

class Test
{
public:
Test(int xx) : x(xx) { cout << x << endl; }

Test add(const Test & obj);

Test(const Test & obj) : x(obj.x)
{ cout << "copy ctor " << x << endl; }

int x;
};

Test Test::add(const Test & obj)
{
return x + obj.x;
}

int main()
{
Test a(10);
Test b(20);

cout << a.add(b).x << endl;

return 0;
}

This program compiles fine and produces the following output
in both g++ and VC++ Express Edition 2005
10
20
30
30

I thought the copy ctor would be called when we invoke a.add(b)
because Test::add() returns an object of Test type and here
I expected the copy ctor to be called.
The compiler is free to optimise away the temporary return object (so
long as doing so doesn't change the behaviour of the program).
 
A

Andrey Tarasevich

Ian said:
The compiler is free to optimise away the temporary return object (so
long as doing so doesn't change the behaviour of the program).

In fact, the compiler is free to optimize away the temporary return object even
if it _does_ change the behavior of the program.
 
J

Juha Nieminen

Test Test::add(const Test & obj)
{
return x + obj.x;
}
I thought the copy ctor would be called when we invoke a.add(b)
because Test::add() returns an object of Test type and here
I expected the copy ctor to be called.

Return value optimization. This is a very common optimization
performed by compilers when the return value of a function is an object.
Basically, what is happening internally is that the calling code gives,
behind the scenes, the function a pointer which basically says
"construct the return value here". Then the function, instead of
creating a local copy for the return value, constructs it directly to
the memory location which was given to it by the calling code.

IOW, if you say for example "Test t = a.add(b);" this will make the
compiler to create space for 't' and give a pointer to Test::add() so
that it can construct the return value into that space directly, thus
avoiding copying anything.

Note that the avoiding the copying is not always possible. For example
in this case the copying usually must be performed:
"extern Test t; t = a.add(b);"
What the compiler does in this case is to create a nameless temporary,
tell the function to construct the return value there, and then it
assigns this temporary to 't'.
 
R

Rolf Magnus

Andrey said:
In fact, the compiler is free to optimize away the temporary return object
even if it _does_ change the behavior of the program.

Which is the reason why the OP didn't see any output from the copy
constructor. The 'missing' output is a change in behavior.
 
S

subramanian100in

Return value optimization. This is a very common optimization
performed by compilers when the return value of a function is an object.
Basically, what is happening internally is that the calling code gives,
behind the scenes, the function a pointer which basically says
"construct the return value here". Then the function, instead of
creating a local copy for the return value, constructs it directly to
the memory location which was given to it by the calling code.

IOW, if you say for example "Test t = a.add(b);" this will make the
compiler to create space for 't' and give a pointer to Test::add() so
that it can construct the return value into that space directly, thus
avoiding copying anything.

Note that the avoiding the copying is not always possible. For example
in this case the copying usually must be performed:
"extern Test t; t = a.add(b);"
What the compiler does in this case is to create a nameless temporary,
tell the function to construct the return value there, and then it
assigns this temporary to 't'.

Do you mean to say, in the statement,
t = a.add(b);
copy ctor will be called first to create the temporary and then the
assignment operator will be called ?

If this is what you meant, then I have to say that it does not happen
with
g++ -std=c++98 -pedantic -Wall -Wextra x.cpp
ie the copy ctor is not called to create the temporary.

Please correct me if I am wrong.

Thanks
V.Subramanian
 
S

subramanian100in

Return value optimization. This is a very common optimization
performed by compilers when the return value of a function is an object.
Basically, what is happening internally is that the calling code gives,
behind the scenes, the function a pointer which basically says
"construct the return value here". Then the function, instead of
creating a local copy for the return value, constructs it directly to
the memory location which was given to it by the calling code.

IOW, if you say for example "Test t = a.add(b);" this will make the
compiler to create space for 't' and give a pointer to Test::add() so
that it can construct the return value into that space directly, thus
avoiding copying anything.

Note that the avoiding the copying is not always possible. For example
in this case the copying usually must be performed:
"extern Test t; t = a.add(b);"
What the compiler does in this case is to create a nameless temporary,
tell the function to construct the return value there, and then it
assigns this temporary to 't'.

Do you mean to say, in the statement,
t = a.add(b);
copy ctor will be called first to create the temporary and then the
assignment operator will be called ?

If this is what you meant, then I have to say that it does not happen
with
g++ -std=c++98 -pedantic -Wall -Wextra x.cpp
ie the copy ctor is not called to create the temporary.

Please correct me if I am wrong.

Thanks
V.Subramanian
 
I

Ian Collins

Do you mean to say, in the statement,
t = a.add(b);
copy ctor will be called first to create the temporary and then the
assignment operator will be called ?
A temporary isn't required, the compiler can assign a to t.
 
K

Kira Yamato

Do you mean to say, in the statement,
t = a.add(b);
copy ctor will be called first to create the temporary and then the
assignment operator will be called ?

If this is what you meant, then I have to say that it does not happen
with
g++ -std=c++98 -pedantic -Wall -Wextra x.cpp
ie the copy ctor is not called to create the temporary.

Please correct me if I am wrong.

This is what is going on. Consider the code

Test t = a.add(b);

Normally, you would think the compiler would do the following:
1) Create a local variable t in the caller function.
2) Call a.add(b), which will create a temporary variable as the result.
3) Call the copy constructor to copy the content in the temporary
variable returned by add() into the local variable t.
4) Call the temporary variable's destructor at the end of the statement.

However, with optimization, this is what the compiler will actually do instead:
a) Call a.add(b), which will create a temporary variable as the result.
b) Have the variable t be referencing the location of the temporary
variable above. No object destructor is called at the end of the
statement. No local variable is created in the stack frame of the
caller function.

Here is proof: With the following code,

#include <iostream>

using namespace std;

class Test
{
public:
Test(int xx) : x(xx) { cout << x << endl; }

Test add(const Test & obj);

Test(const Test & obj) : x(obj.x)
{ cout << "copy ctor " << x << endl; }

~Test()
{ cout << "destroy " << x << endl; }

int x;
};

Test Test::add(const Test & obj)
{
Test y(x + obj.x);
cout << "Test::add(). &y=" << &y << endl;
return y;
}

int main()
{
Test a = Test(10);
Test b = a.add(a);
cout << "main(). &b=" << &b << endl;
return 0;
}

=====================

The output of this code is:

10
20
Test::add(). &y=0xbffff8fc
main(). &b=0xbffff8fc
destroy 20
destroy 10

=====================

Note that the addresses of y and b are the same.
 
J

James Kanze

A temporary isn't required, the compiler can assign a to t.

I certainly hope not. The formal semantics here are clear:

the return statement constructs a return value by implicitly
converting the results of x + obj.x to a (temporary) Test,

that temporary is copied to whatever location is used for
value return (which is also a temporary), and

the const reference parameter of the assignment operator is
bound to that returned value.

The compiler is allowed to suppress the copy, and construct the
temporary in the return statement directly in its final
location. But it definitly needs a temporary to bind to the
reference in the assignment operator.

The compiler cannot assign a to t, since the return value of
Test::add is not a copy of a.
 
J

Juha Nieminen

Do you mean to say, in the statement,
t = a.add(b);
copy ctor will be called first to create the temporary and then the
assignment operator will be called ?

Not really. With "copying" I was referring to the assignment.

There are probably similar ways to "force" a true copy constructor
call even when return value optimization is in use.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top