Question regarding copy constructor

S

somenath

I have one question regarding the output of the following program.

#include<iostream>
using namespace std;

class A {
public:
A(int x) {
cout<<"Inside constructor A "<<endl;

}
A(const A&a) {
cout<<"Inside copy constructor";
}
void operator =( int x) {
cout<<"Inside assignment operator overloading "<<endl;
}
};
int main(void)
{
A a(2);
A a1 = 2;
A a2 = a;
return 0;
}
Output
+++++++++
Inside constructor A
Inside constructor A
Inside copy constructor

For this code
A a1 = 2;
I was expecting the copy constructor would be called. Because my understanding is compiler will create a temporary object using 2 and then that temporary object will be copied to a1 object. Something as follows.
__tempObj(2);
A a1 = __tempObj;
But that is not happening here . The copy constructor is not getting called. Where am I going wrong?
How the compiler then assigning 2 to object a1?
 
A

Alf P. Steinbach

The copy constructor is not getting called. Where am I going wrong?

No matter what a copy constructor does, the compiler is allowed to
assume, for purposes of optimization, that it does nothing but copying
the object, and that it does that perfectly.

The copy constructor is the only such special case, where the compiler
can just disregard the actual code and assume a perfect implementation.

Thus, when you have a declaration like

A a = 2;

then the formal meaning is

A a( A( 2 ) );

that is, a copy construction.

But while that means that the copy constructor must be ACCESSIBLE, the
compiler is allowed to optimize this to

A a( 2 );

that is, a direct initialization, even if that fails to produce some
intended side effect in the copy constructor.

You can find the formal rules for such COPY ELISION in C++11 §12.8/31.


Cheersw & hth.,

- Alf
 
K

K. Frank

Hi Alf!

...
Thus, when you have a declaration like

A a = 2;

then the formal meaning is

A a( A( 2 ) );

that is, a copy construction.

But while that means that the copy constructor must be ACCESSIBLE, the
compiler is allowed to optimize this to

A a( 2 );

that is, a direct initialization, even if that fails to produce some
intended side effect in the copy constructor.

I agree that this is the case, but I've often
wondered why.

Given that in somenath's example, the constructor
A(int) exists, and given that the compiler is
allowed to copy-elide the copy constructor away,
why does the standard _not_ define

A a = 2;

and

A a(2);

to be synonymous? That is, why not just define
A a = 2 to be direct initialization, and be done
with it?

Given that the programmer cannot rely on the copy
constructor getting called in this situation, I
don't see any reason why letting the compiler
first construct and then copy construct would be
advantage. Wouldn't it be simpler and better (and
fully backward compatible) just to require A a = 2
to be direct initialization?

Just wondering ...
...
Cheersw & hth.,

- Alf


Thanks.


K. Frank
 
T

Tobias Müller

K. Frank said:
Given that in somenath's example, the constructor
A(int) exists, and given that the compiler is
allowed to copy-elide the copy constructor away,
why does the standard _not_ define

A a = 2;

and

A a(2);

to be synonymous? That is, why not just define
A a = 2 to be direct initialization, and be done
with it?

Given that the programmer cannot rely on the copy
constructor getting called in this situation, I
don't see any reason why letting the compiler
first construct and then copy construct would be
advantage. Wouldn't it be simpler and better (and
fully backward compatible) just to require A a = 2
to be direct initialization?

Even more so as it is already a special case. Regarding only the syntax,
you would expect the appropriate assignment operator to be called.

Tobi
 
Ö

Öö Tiib

[...]
Given that in somenath's example, the constructor
A(int) exists, and given that the compiler is
allowed to copy-elide the copy constructor away,
why does the standard _not_ define

A a = 2;

and

A a(2);

to be synonymous? That is, why not just define
A a = 2 to be direct initialization, and be done
with it?

Given that the programmer cannot rely on the copy
constructor getting called in this situation, I
don't see any reason why letting the compiler
first construct and then copy construct would be
advantage. Wouldn't it be simpler and better (and
fully backward compatible) just to require A a = 2
to be direct initialization?

Even more so as it is already a special case. Regarding only the syntax,
you would expect the appropriate assignment operator to be called.

There certainly won't be assignment operators used in initialization. Most
coding standards suggest to write what you actually want 'A a(2);' and
to avoid writing "likely synonymous after optimizations" variants.
 
T

Tobias Müller

Öö Tiib said:
[...]
Given that in somenath's example, the constructor
A(int) exists, and given that the compiler is
allowed to copy-elide the copy constructor away,
why does the standard _not_ define

A a = 2;

and

A a(2);

to be synonymous? That is, why not just define
A a = 2 to be direct initialization, and be done
with it?

Given that the programmer cannot rely on the copy
constructor getting called in this situation, I
don't see any reason why letting the compiler
first construct and then copy construct would be
advantage. Wouldn't it be simpler and better (and
fully backward compatible) just to require A a = 2
to be direct initialization?

Even more so as it is already a special case. Regarding only the syntax,
you would expect the appropriate assignment operator to be called.

There certainly won't be assignment operators used in initialization.

Yes, that's what I wanted to say. Syntactically it's an assignment, but in
the context of a declaration it is special cased to actually mean
initialization. But then, why in a such convoluted way?
Most coding standards suggest to write what you actually want 'A a(2);' and
to avoid writing "likely synonymous after optimizations" variants.

The above proposal would provide exactly that. Without relying on
optimizations.
But I agree, just writing the appropriate constructor is even better.

Tobi
 
V

Victor Bazarov

[..] Syntactically it's an assignment, but in
the context of a declaration it is special cased to actually mean
initialization. But then, why in a such convoluted way?

<shrug> Why not? This is one of C legacies. It's very likely that in a
function scope it literally meant the same machine code... And many an
older folk got used to seeing this form and keep using it. Imagine (a)
requiring to rewrite millions of lines(*) of

int a = 42;

to

int a { 42 };

and (b) force scores of thousands of C++ programmers to re-learn to use
that form from some point on... I don't think it's feasible.

*) I don't really know how many lines of such code exist and need to be
maintained, so might be off by a billion or two.

V
 
J

James Kanze

Yes, that's what I wanted to say. Syntactically it's an assignment, but in
the context of a declaration it is special cased to actually mean
initialization.

Syntactically, it isn't an assignment. And assignment can only
occur as part of an expression, and the = sign there is *not*
part of any expression.

There are several character sequences that can be both operators
and punctuation, depending on context: the comma is the most
noted, but a single colon is also ambiguous in this regard.
That's obviously not an ideal situation, but it's one we have to
live with for historical reasons.
But then, why in a such convoluted way?

I've seen coding standards suggest just the opposite. With copy
initialization, you'll never encounter C++'s most vexing parse.
 
T

Tobias Müller

James Kanze said:
Syntactically, it isn't an assignment. And assignment can only
occur as part of an expression, and the = sign there is *not*
part of any expression.

Yes I'm inprecise. It _looks_ like an assignment and is a special case in
the syntax. Better?
There are several character sequences that can be both operators
and punctuation, depending on context: the comma is the most
noted, but a single colon is also ambiguous in this regard.
That's obviously not an ideal situation, but it's one we have to
live with for historical reasons.


I've seen coding standards suggest just the opposite. With copy
initialization, you'll never encounter C++'s most vexing parse.

But that still does not explain why
A a = 2;
is interpreted as
A a(A(2));
and not just
A a(2);

Tobi
 
Ö

Öö Tiib

I've seen coding standards suggest just the opposite. With copy
initialization, you'll never encounter C++'s most vexing parse.

Ok, that makes perfect sense without tools that can warn about likely
most vexing parse. How often does anyone actually want to declare a
function in other function's body? I have had very close to zero
occasions of such desire. So most vexing parse is accidental usage
of useless feature and a tool that underlines it with red does
not take any rocket science to develop.
 
R

Rosario1903

Yes I'm inprecise. It _looks_ like an assignment and is a special case in
the syntax. Better?


But that still does not explain why
A a = 2;
is interpreted as
A a(A(2));
and not just
A a(2);

A a=2;

would be:
A::a() or don't know how they say that default constructor for A
applied on "a" with 0 arg
a=2 assign to "a" the value 2
 
Ö

Öö Tiib

A a=2;

would be:

A::a() or don't know how they say that default constructor for A
applied on "a" with 0 arg
a=2 assign to "a" the value 2

Are you suggesting such change? If you suggest it as change then that won'thappen. Silent
change of behaviour won't be accepted into C++.

What Tobias suggests is what most C++ compilers do anyway because it is allowed. So I
also think that ... why not?
 
Ö

Öö Tiib

Then you get problems like this:

struct A { ... };
struct Comparator { bool operator()(const A&, const A&) const { ... } };

std::map<A, int, Comparator> theMap(Comparator());

Should add double parentheses somewhere:

std::map said:
(Can't see what the problem is? Try calling one of the member functions
of theMap...)

The people who don't see it with bare eye should use -Wvexing-parse of clang.
I'm still thinking that there is powerful enemy rat in standard commission. Every
time they mess with initialisation they add more confusing and ambiguous
syntaxes. The new 'A a{2};' and 'A a = {2};' are screwed up as well.
 
T

Tobias Müller

Rosario1903 said:
^^^^^^^^^^^
constructor for A applied on "a" with 0 arg



with operator=

We're going in circles.
Even if it looks like an assignment, it is in fact initialization. There is
no operator= involved.

I understand that this is legacy from C. What I don't understand is why
they chose copy initialization instead of direct initialization.

Tobi
 
T

Tobias Müller

Juha Nieminen said:
Then you get problems like this:

struct A { ... };
struct Comparator { bool operator()(const A&, const A&) const { ... } };

std::map<A, int, Comparator> theMap(Comparator());

(Can't see what the problem is? Try calling one of the member functions
of theMap...)

Actually, I can't right now. I have also no compiler at hand.

But I don't see how it would be relevant either. I was not proposing a
macro-like (text based) expansion from:
A a = 2;
To:
A a(2);

The second form would never occur in the source and wouldn't have to be
parsed.
What I proposed was to interpret:
A a = 2;
as:
"Call of a suitable constructor with the RHS as direct argument"

Tobi
 
R

Rosario1903

We're going in circles.
Even if it looks like an assignment, it is in fact initialization. There is
no operator= involved.

I understand that this is legacy from C. What I don't understand is why
they chose copy initialization instead of direct initialization.

is it so, because one *see* the same that is executed?
int a=3;
would mean reserve mem for a, and fill with 3 value
in C++=> use one constructor for a, and use operator= for copy that

if i like to use copy constructor i would use A b,a(b);
if i like to use constructor int A a(2);
if i like to use A::A() constructor and operator=(int ) A a=2
etc

but i know that all is already done, and C++ already call all i not
remember because i not study all that and i not have very very good
memory etc
 
T

Tobias Müller

K. Frank said:
Hi Alf!



I agree that this is the case, but I've often
wondered why.

Given that in somenath's example, the constructor
A(int) exists, and given that the compiler is
allowed to copy-elide the copy constructor away,
why does the standard _not_ define

A a = 2;

and

A a(2);

to be synonymous? That is, why not just define
A a = 2 to be direct initialization, and be done
with it?

Given that the programmer cannot rely on the copy
constructor getting called in this situation, I
don't see any reason why letting the compiler
first construct and then copy construct would be
advantage. Wouldn't it be simpler and better (and
fully backward compatible) just to require A a = 2
to be direct initialization?

Could it be that:
B b;
A a = b;
is not actually equivalent to:
B b;
A a(A(b));
but rather to:
"Explicitly call the copy constructor of A with b as argument."
which is equivalent to:
A a(implicit_cast<const A&>(b));

This would also take conversion operators into account, not only conversion
constructors.

implicit_cast would be defined as:
template<typename A>
A implicit_cast(A a) { return a; }
or similar

Tobi
 
K

K. Frank

Hello Tobi!

...
Could it be that:

B b;
A a = b;
is not actually equivalent to:
B b;
A a(A(b));
but rather to:
"Explicitly call the copy constructor of A with b as argument."
which is equivalent to:
A a(implicit_cast<const A&>(b));

This would also take conversion operators into account, not only conversion
constructors.
...

I think you're probably right about this. (At least
it sounds good.)

Just to be clear, what I think you're saying is that
if the conversion operator:

B::eek:perator A()

exists, but the constructor:

A::A(B)

does not, then (as currently defined)

A a = b;

will work, because the conversion operator exists,
but if we defined A a = b; to be exactly synonymous
with direct initialization, then it would fail,
because the necessary constructor doesn't exist.

Is that right?

(Perhaps I will take a look at the standard and/or
write a test program to sort this out.)

By the way, this may be relevant to the recent post
"Understanding copy construction ambiguity" on our
moderated companion group:

https://groups.google.com/forum/?fromgroups#!topic/comp.lang.c++.moderated/-5pL9ywO29Y

Perhaps somebody who understands this better than I
do could shed some light on that fellow's question.


Thanks.


K. Frank
 
T

Tobias Müller

K. Frank said:
Hello Tobi!



I think you're probably right about this. (At least
it sounds good.)

Just to be clear, what I think you're saying is that
if the conversion operator:

B::eek:perator A()

exists, but the constructor:

A::A(B)

does not, then (as currently defined)

A a = b;

will work, because the conversion operator exists,
but if we defined A a = b; to be exactly synonymous
with direct initialization, then it would fail,
because the necessary constructor doesn't exist.

Is that right?

That's what I was thinking, though I am not a language lawyer.
(Perhaps I will take a look at the standard and/or
write a test program to sort this out.)

By the way, this may be relevant to the recent post
"Understanding copy construction ambiguity" on our
moderated companion group:

https://groups.google.com/forum/?fromgroups#!topic/comp.lang.c++.moderated/-5pL9ywO29Y

Yeah, reading that post actually made me thinking in that direction.
Perhaps somebody who understands this better than I
do could shed some light on that fellow's question.

I would have to think a bit more about it.

Tobi
 

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

Forum statistics

Threads
473,774
Messages
2,569,599
Members
45,175
Latest member
Vinay Kumar_ Nevatia
Top