copy constructor question

  • Thread starter Kurt Krueckeberg
  • Start date
K

Kurt Krueckeberg

Given a class X
class X {
public: X(int);
X(const X&); //. . .
};

Is this line
X x1(1);
the same thing as
X x2 = 2;

That is, does the initialization of x2 result in a call the copy constructor
and nothing else, period. Is that what the standard says should happen?

Or, is the code generated for x2(2) more properly equivalent, according to
the standard, to something like
X tmp(2);
X x2(tmp);
tmp.~X();
with the difference being that the compiler may simply optimize away the
creation of the temporary, so the initialization x2 results in just a
single call to the copy constructor.

thanks
 
V

Victor Bazarov

Kurt said:
Given a class X
class X {
public: X(int);
X(const X&); //. . .
};

Is this line
X x1(1);
the same thing as
X x2 = 2;
No.

That is, does the initialization of x2 result in a call the copy constructor
and nothing else, period. Is that what the standard says should happen?

The second case is interpreted as

X x2(X(2));

IOW, a copy c-tor is invoked after a parameterised c-tor creates
a temporary object. The compiler is allowed to skip the creation of
the temporary, but the copy c-tor has to be available as if it would
be used.
Or, is the code generated for x2(2) more properly equivalent, according to
the standard, to something like
X tmp(2);
X x2(tmp);
tmp.~X();

Yes. Kind of. Your 'tmp' should be const.
with the difference being that the compiler may simply optimize away the
creation of the temporary, so the initialization x2 results in just a
single call to the copy constructor.

Actually, initialisation of x2 results in just a single call to the
parameterised constructor, not copy constructor.

V
 
R

Rob Williscroft

Kurt Krueckeberg wrote in in
comp.lang.c++:
Given a class X
class X {
public: X(int);
X(const X&); //. . .
};

Is this line
X x1(1);

This is called "Direct initialization"
the same thing as
X x2 = 2;

This is called "Copy initialization" as it potentialy uses the copy
constructor and requires (even if it isn't used) that it be accessable.
That is, does the initialization of x2 result in a call the copy
constructor and nothing else, period. Is that what the standard says
should happen?

Or, is the code generated for x2(2) more properly equivalent,
according to the standard, to something like
X tmp(2);
X x2(tmp);
tmp.~X();
with the difference being that the compiler may simply optimize away
the creation of the temporary, so the initialization x2 results in
just a single call to the copy constructor.

Yes, but the copy constructor is (possibly) elided, i.e. its removed
before any optimization occurs, and can be removed even if it has side
effects (i.e. in cases that no good optimizer would remove it). This
is explicitly alowed by the standard.

HTH.

Rob.
 
R

Rob Williscroft

Rob Williscroft wrote in 130.133.1.4 in comp.lang.c++:
Kurt Krueckeberg wrote in in
comp.lang.c++:

Whoopse I missread the above line, the code below and my response
is related to X x2 = 2.
Yes, but the copy constructor is (possibly) elided, i.e. its removed
before any optimization occurs, and can be removed even if it has side
effects (i.e. in cases that no good optimizer would remove it). This
is explicitly alowed by the standard.

Rob.
 
E

E. Robert Tisdale

Kurt said:
Given a class X
class X {
public:
X(int);
X(const X&); //. . .
};
is this line
the same thing as
X x2 = 2;
Yes.

That is, does the initialization of x2 result
in a call the copy constructor and nothing else, period.
No.

Is that what the standard says should happen?

Or, is the code generated for x2(2) more properly equivalent,
according to the standard, to something like
X tmp(2);
X x2(tmp);
tmp.~X();
with the difference being that
the compiler may simply optimize away the creation of the temporary, so [that]
the initialization x2 results in just a single call to the copy constructor.
cat main.cc
#include <iostream>

class X {
private:
// representation
int I;
public:
// functions
friend
std::eek:stream& operator<<(std::eek:stream& os, const X& x) {
return os << x.I;
}
// constructors
X(int i = 0): I(i) {
std::clog << "X::X(int), I = " << I << std::endl;
}
X(const X& x): I(x.I) {
std::clog << "X::X(const X&)" << std::endl;
}
~X(void) {
std::clog << "X::~X(void), I = " << I << std::endl;
}
};

int main(int argc, char* argv[]) {
X x1(1);
std::cout << "x1 = " << x1 << std::endl;
X x2 = 2;
std::cout << "x2 = " << x2 << std::endl;
return 0;
}
g++ -Wall -ansi -pedantic -o main main.cc
./main
X::X(int), I = 1
x1 = 1
X::X(int), I = 2
x2 = 2
X::~X(void), I = 2
X::~X(void), I = 1

The copy constructor is *never* called.
 
B

Bob Hairgrove

On Tue, 02 Nov 2004 11:17:23 -0800, "E. Robert Tisdale"

[snipped class X definition]
int main(int argc, char* argv[]) {
X x1(1);
std::cout << "x1 = " << x1 << std::endl;
X x2 = 2;
std::cout << "x2 = " << x2 << std::endl;
return 0;
}
g++ -Wall -ansi -pedantic -o main main.cc
./main
X::X(int), I = 1
x1 = 1
X::X(int), I = 2
x2 = 2
X::~X(void), I = 2
X::~X(void), I = 1

The copy constructor is *never* called.

If you change it just a little bit, it will be called:

#include <iostream>
#include <ostream>
// some implementations may not automatically
// include ostream with iostream.

class X {
private:
// representation
int I;
public:
// functions
friend
std::eek:stream& operator<<(std::eek:stream& os, const X& x) {
return os << x.I;
}
// constructors
X(int i = 0): I(i) {
std::clog << "X::X(int), I = " << I << std::endl;
}
X(const X& x): I(x.I) {
std::clog << "X::X(const X&)" << std::endl;
}
//------------------------------------------------
// assignment operator added, which is not called:
//------------------------------------------------
X& operator=(const X& x) {
I = x.I;
std::clog << "X::eek:perator=(const X& x)" << std::endl;
return *this;
}

~X(void) {
std::clog << "X::~X(void), I = " << I << std::endl;
}
};

int main() {
X x1(1);
std::cout << "x1 = " << x1 << std::endl;
X x2 = 2;
std::cout << "x2 = " << x2 << std::endl;
// notice how the copy ctor, and not the assignment
// operator, is called here:
X x3 = x1;
std::cout << "x3 = " << x3 << std::endl;
return 0;
}

Output:

X::X(int), I = 1
x1 = 1
X::X(int), I = 2
x2 = 2
X::X(const X&)
x3 = 1
X::~X(void), I = 1
X::~X(void), I = 2
X::~X(void), I = 1
 
L

Larry Brasfield

Corrections suggested below.

E. Robert Tisdale said:

I strongly recommend that you read your C++ reference
a little more closely. If it justifies the above answer, toss
it in the trash. Mr. Bazarov's answer is correct.
That is, does the initialization of x2 result
in a call the copy constructor and nothing else, period.
No.

Is that what the standard says should happen?

Or, is the code generated for x2(2) more properly equivalent, according to the standard, to something like
X tmp(2);
X x2(tmp);
tmp.~X();
with the difference being that the compiler may simply optimize away the creation of the temporary, so [that] the initialization
x2 results in just a single call to the copy constructor.
cat main.cc
#include <iostream>

class X {
private:
// representation
int I;
public:
// functions
friend
std::eek:stream& operator<<(std::eek:stream& os, const X& x) {
return os << x.I;
}
// constructors
X(int i = 0): I(i) {
std::clog << "X::X(int), I = " << I << std::endl;
}
X(const X& x): I(x.I) {
std::clog << "X::X(const X&)" << std::endl;
}
~X(void) {
std::clog << "X::~X(void), I = " << I << std::endl;
}
};

int main(int argc, char* argv[]) {
X x1(1);
std::cout << "x1 = " << x1 << std::endl;
X x2 = 2;
std::cout << "x2 = " << x2 << std::endl;
return 0;
}
g++ -Wall -ansi -pedantic -o main main.cc
./main
X::X(int), I = 1
x1 = 1
X::X(int), I = 2
x2 = 2
X::~X(void), I = 2
X::~X(void), I = 1

The copy constructor is *never* called.

You need to understand that optimizations a particular
compiler may perform do not constitute a valid prediction
for what C++ compilers in general will do. Try making
the copy ctor private and see if your answer survives.
 
E

E. Robert Tisdale

Victor said:
The second case is interpreted as

X x2(X(2));

IOW, a copy c-tor is invoked after a parameterised c-tor creates
a temporary object.
The compiler is allowed to skip the creation of the temporary,
but the copy c-tor has to be available as if it would be used.

No!

The compiler is allowed to *elide* the copy constructor
and and call X(int) to initialize x2 directly
as if the programmer had written:

X x2(2);
 
V

Victor Bazarov

E. Robert Tisdale said:

What "No"?!
The compiler is allowed to *elide* the copy constructor
and and call X(int) to initialize x2 directly
as if the programmer had written:

X x2(2);

I am tired of your "No!" that stems from your lack of knowledge, Bob.
Read the Standard, section 8.5, paragraph 14, forth bullet, third sub-
bullet, beginning with the words "Otherwise (i.e., for the remaining".
If the conversion cannot be done or is ambiguous, the initialisation is
ill-formed. IOW,

struct X {
X(int);
private:
X(const X&);
};

int main() {
X x = 2;
}

should NOT compile, even with your favourite g++, while

struct X {
X(int);
private:
X(const X&);
};

int main() {
X x(2);
}

will compile fine.

....And don't come back to this thread until you learn this by heart.

V
 
A

Andrew Koenig

E. Robert Tisdale said:

No.

When you write

X x1(1);

only one object is created, namely x1. That object gets 1 as its
constructor argument.

When you write

X x2 = 2;

two objects are nominally created. The first one is an unnamed temporary of
type X, and gets 2 as its constructor argument. The second is x2, which is
created as a copy of the temporary. The effect is similar to this:

X temp(2);
X x2(temp);

except that temp is then destroyed, instead of waiting for the destruction
of x2.

As an optimization, the compiler is permitted to eliminate the temporary,
and construct x2 directly. However, it is not required to do so. Moreover,
whether or not the compiler eliminates the temporary, class X must have a
copy constructor for the definition of x2. It need not have a copy
constructor for the definition of x1, because nothing in that definition
uses the copy constructor.
 
E

E. Robert Tisdale

No.
The implementation is allowed to create a temporary object X(2)
then call the copy constructor to copy the temporary into x2
but it isn't required to do so.

elide the copy constructor and call X(int) to initialize x2 directly

The class library developer need not define a copy constructor.
The implementation will provide one but might not ever call it.
But, if the class library developer provides a copy constructor,
it must be public even if it is elided in the definition of x2 above.
What "No"?!


I am tired of your "No!" that stems from your lack of knowledge, Bob.

You left Kurt and me with the impression
that the copy constructor *must* be invoked.
Read the Standard, section 8.5, paragraph 14, forth bullet, third sub-
bullet, beginning with the words "Otherwise (i.e., for the remaining".
If the conversion cannot be done or is ambiguous, the initialisation is
ill-formed. IOW,

struct X {
X(int);
private:
X(const X&);
};

int main() {
X x = 2;
}

should NOT compile, even with your favourite g++
cat main.cc
#include <iostream>

class X {
private:
// representation
int I;
public:
// operators
friend
std::eek:stream& operator<<(std::eek:stream& os, const X& x) {
return os << x.I;
}
X& operator=(const X& x) {
I = x.I;
std::clog << "X::eek:perator=(const X& x)" << std::endl;
return *this;
}
// constructors
X(int i = 0): I(i) {
std::clog << "X::X(int), I = " << I << std::endl;
}
~X(void) {
std::clog << "X::~X(void), I = " << I << std::endl;
}
private:
X(const X& x): I(x.I) {
std::clog << "X::X(const X&)" << std::endl;
}
};

int main(int argc, char* argv[]) {
X x1(1);
std::cout << "x1 = " << x1 << std::endl;
X x2 = 2;
std::cout << "x2 = " << x2 << std::endl;
return 0;
}
g++ -Wall -ansi -pedantic -o main main.cc
main.cc: In function `int main(int, char**)':
main.cc:26: error: `X::X(const X&)' is private
main.cc:34: error: within this context
main.cc:26: error: `X::X(const X&)' is private
main.cc:34: error: within this context
main.cc:34: error: initializing temporary \
from result of `X::X(int)'
mv main.cc main.ccold
cp main.ccold main.cc
vi main.cc
diff main.ccold main.cc
25d24
< private:
g++ -Wall -ansi -pedantic -o main main.cc
./main
X::X(int), I = 1
x1 = 1
X::X(int), I = 2
x2 = 2
X::~X(void), I = 2
X::~X(void), I = 1
vi main.cc
diff main.ccold main.cc
25,28d24
< private:
< X(const X& x): I(x.I) {
< std::clog << "X::X(const X&)" << std::endl;
< }
g++ -Wall -ansi -pedantic -o main main.cc
./main
X::X(int), I = 1
x1 = 1
X::X(int), I = 2
x2 = 2
X::~X(void), I = 2
X::~X(void), I = 1
vi main.cc
diff main.ccold main.cc 18a19
explicit
25d25
< private:
g++ -Wall -ansi -pedantic -o main main.cc
main.cc: In function `int main(int, char**)':
main.cc:34: error: conversion from `int' \
to non-scalar type `X' requested
 
E

E. Robert Tisdale

Andrew said:
No.

When you write

X x1(1);

only one object is created, namely x1.
That object gets 1 as its constructor argument.

When you write

X x2 = 2;
two objects are nominally created.
The first one is an unnamed temporary of type X,
and gets 2 as its constructor argument.
The second is x2, which is created as a copy of the temporary.
The effect is similar to this:
X temp(2);
X x2(temp);

except that temp is then destroyed,
instead of waiting for the destruction of x2.

No!
The standard does *not* require this
and it was a mistake to allow this interpretation in the first place.
As an optimization, the compiler is permitted to eliminate the temporary,
and construct x2 directly. However, it is not required to do so.
Moreover, whether or not the compiler eliminates the temporary,
class X must have a [public] copy constructor for the definition of x2.

It need not have a copy constructor for the definition of x1,
because nothing in that definition uses the copy constructor.

The problem with your description is that
it is exactly backwards from actual implementation --
the de facto standard.

The temporary object, the copy constructor
and the subsequent destruction of the temporary are unnecessary
and are elided by default in the typical implementation.
Only inferior implementations fail to perform this optimization.

If your C++ compiler fails to implement this optimization,
you should be shopping for a better optimizing C++ compiler.

You description is misleading and confusing
as evidenced by Kurt Krueckeberg question.

Kurt should interpret

X x2 = 2;

as

X x2(2);

but be warned about inferior C++ compilers.
 

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
474,183
Messages
2,570,967
Members
47,517
Latest member
Andres38A1

Latest Threads

Top