Discussion:
[std-discussion] Copy elision for list initialization
'Johannes Schaub' via ISO C++ Standard - Discussion
2016-08-06 08:42:04 UTC
Permalink
During a stackoverflow question at
http://stackoverflow.com/questions/38801955/how-does-guaranteed-copy-elision-work-in-list-initialization-in-c1z,
I was of the opinion that copy elision of a list initialization by a
copy constructor of a non-aggregate class type should not be possible
by current C++14 rules. But both GCC and Clang elide the copy
constructor in this example:

#include <iostream>

struct A {
A() { }

A(const A& ) {
std::cout << "not elided\n";
}
};

A f() { return A(); }
A g() { return {A()}; }

int main() {
f();
std::cout << "now g\n";
g();
}

None print "not elided" for the call to g(). Why is that? The elision
paragraph says

"when a temporary class object that has not been bound to a
reference (12.2) would be copied/moved to a class object with the same
cv-unqualified type, the copy/move operation can be omitted by
constructing the temporary object directly into the target of the
omitted copy/move"

This means that the copy of "A()" into a by-value parameter of a
constructor of "A" can be elided.. but not the call to the constructor
of "A" itself!? So are GCC and Clang conforming here?
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Robert Haberlach
2016-08-06 10:08:55 UTC
Permalink
The copy constructor will be called with `A()` for both f and g (i.e. A() is copied into the return value temporary), so I don't understand how you
distinguish between them for this matter?
Post by 'Johannes Schaub' via ISO C++ Standard - Discussion
During a stackoverflow question at
http://stackoverflow.com/questions/38801955/how-does-guaranteed-copy-elision-work-in-list-initialization-in-c1z,
I was of the opinion that copy elision of a list initialization by a
copy constructor of a non-aggregate class type should not be possible
by current C++14 rules. But both GCC and Clang elide the copy
#include <iostream>
struct A {
A() { }
A(const A& ) {
std::cout << "not elided\n";
}
};
A f() { return A(); }
A g() { return {A()}; }
int main() {
f();
std::cout << "now g\n";
g();
}
None print "not elided" for the call to g(). Why is that? The elision
paragraph says
"when a temporary class object that has not been bound to a
reference (12.2) would be copied/moved to a class object with the same
cv-unqualified type, the copy/move operation can be omitted by
constructing the temporary object directly into the target of the
omitted copy/move"
This means that the copy of "A()" into a by-value parameter of a
constructor of "A" can be elided.. but not the call to the constructor
of "A" itself!? So are GCC and Clang conforming here?
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
'Johannes Schaub' via ISO C++ Standard - Discussion
2016-08-06 12:00:39 UTC
Permalink
The copy constructor will be called during initialization, but is the whole
list-initialization then a copy and/or move operation? I thought a copy
operation is only one if it happens for initialization, and not if someone
happens by chance to call the copy constructor. Paragraph 1 says

"A class object can be copied or moved in two ways: by initialization
(12.1, 8.5), including for function argument passing (5.2.2) and for
function value return (6.6.3); and by assignment (5.17). ". However, for
list-initialization, the source-type of the initialization is not defined
("If the initializer is not a single (possibly parenthesized) expression,
the source type is not defined"). So how can the requirement "would be
copied or moved to a class object with the same type" be satisfied?
Post by Robert Haberlach
The copy constructor will be called with `A()` for both f and g (i.e. A()
is copied into the return value temporary), so I don't understand how you
distinguish between them for this matter?
Post by 'Johannes Schaub' via ISO C++ Standard - Discussion
During a stackoverflow question at
http://stackoverflow.com/questions/38801955/how-does-guaranteed-copy-elision-work-in-list-initialization-in-c1z,
Post by 'Johannes Schaub' via ISO C++ Standard - Discussion
I was of the opinion that copy elision of a list initialization by a
copy constructor of a non-aggregate class type should not be possible
by current C++14 rules. But both GCC and Clang elide the copy
#include <iostream>
struct A {
A() { }
A(const A& ) {
std::cout << "not elided\n";
}
};
A f() { return A(); }
A g() { return {A()}; }
int main() {
f();
std::cout << "now g\n";
g();
}
None print "not elided" for the call to g(). Why is that? The elision
paragraph says
"when a temporary class object that has not been bound to a
reference (12.2) would be copied/moved to a class object with the same
cv-unqualified type, the copy/move operation can be omitted by
constructing the temporary object directly into the target of the
omitted copy/move"
This means that the copy of "A()" into a by-value parameter of a
constructor of "A" can be elided.. but not the call to the constructor
of "A" itself!? So are GCC and Clang conforming here?
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
'Johannes Schaub' via ISO C++ Standard - Discussion
2016-08-06 12:24:50 UTC
Permalink
In other words, there are two initializations. One is the list
initialization, and the other is the initialization of the parameter of the
selected constructor. If that parameter would be a by-value parameter of
"A" (which is not allowed), copy elision could apply for *that*, but the
outer initialization is not apparently to me a "copy", since the
initializer is "{A()}", far from being an object of the same type.

Note that for aggregates, we have a rule in C++17 probably that says that
if we are initializing a class type aggregate, and the single list item has
the same type, then we are initializing the target aggregate with the
single list item (in response to a DR that would treat the single list item
as an initializer for a member of the aggregate). Then *that* nested
initialization could be copy elided and by that eliminate the whole
initialization. But I don't see how my initial non-aggregate would benefit
from copy elision.
Post by Robert Haberlach
The copy constructor will be called with `A()` for both f and g (i.e. A()
is copied into the return value temporary), so I don't understand how you
distinguish between them for this matter?
Post by 'Johannes Schaub' via ISO C++ Standard - Discussion
During a stackoverflow question at
http://stackoverflow.com/questions/38801955/how-does-guaranteed-copy-elision-work-in-list-initialization-in-c1z,
Post by 'Johannes Schaub' via ISO C++ Standard - Discussion
I was of the opinion that copy elision of a list initialization by a
copy constructor of a non-aggregate class type should not be possible
by current C++14 rules. But both GCC and Clang elide the copy
#include <iostream>
struct A {
A() { }
A(const A& ) {
std::cout << "not elided\n";
}
};
A f() { return A(); }
A g() { return {A()}; }
int main() {
f();
std::cout << "now g\n";
g();
}
None print "not elided" for the call to g(). Why is that? The elision
paragraph says
"when a temporary class object that has not been bound to a
reference (12.2) would be copied/moved to a class object with the same
cv-unqualified type, the copy/move operation can be omitted by
constructing the temporary object directly into the target of the
omitted copy/move"
This means that the copy of "A()" into a by-value parameter of a
constructor of "A" can be elided.. but not the call to the constructor
of "A" itself!? So are GCC and Clang conforming here?
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Nicol Bolas
2016-08-06 14:09:20 UTC
Permalink
Post by 'Johannes Schaub' via ISO C++ Standard - Discussion
During a stackoverflow question at
http://stackoverflow.com/questions/38801955/how-does-guaranteed-copy-elision-work-in-list-initialization-in-c1z,
I was of the opinion that copy elision of a list initialization by a
copy constructor of a non-aggregate class type should not be possible
by current C++14 rules. But both GCC and Clang elide the copy
#include <iostream>
struct A {
A() { }
A(const A& ) {
std::cout << "not elided\n";
}
};
A f() { return A(); }
A g() { return {A()}; }
int main() {
f();
std::cout << "now g\n";
g();
}
None print "not elided" for the call to g(). Why is that? The elision
paragraph says
"when a temporary class object that has not been bound to a
reference (12.2) would be copied/moved to a class object with the same
cv-unqualified type, the copy/move operation can be omitted by
constructing the temporary object directly into the target of the
omitted copy/move"
This means that the copy of "A()" into a by-value parameter of a
constructor of "A" can be elided.. but not the call to the constructor
of "A" itself!? So are GCC and Clang conforming here?
in a return statement in a function with a class return type, when the
*expression* is the name of a non-volatile automatic object

A braced-init-list is not an expression. So by this rule, returning a
braced-init-list would not qualify for elision. And thus, those compilers
are eliding things which they are not allowed to elide.

Should this be considered a defect? I don't know; how often are people
going to use `return {prvalue};` where `prvalue` is the same type as the
function's return type?
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
'Johannes Schaub' via ISO C++ Standard - Discussion
2016-08-06 15:14:43 UTC
Permalink
I think you are looking somewhere wrong. Note that I was quoting C++14, and
it had another bullet which allowed elision for copies of temporaries. What
you quoted is the copy elision of a named object return instead. C++17
moved the temporary copy elision into clause 8 and made it a required
elision. For C++17, it seems even obvious that list initialization of a
non-aggregate object from a temporary can't apply copy elision because they
moved the elision to clause 8 after the bullet that talks about
initialization from an rvalue.
Post by Nicol Bolas
Post by 'Johannes Schaub' via ISO C++ Standard - Discussion
During a stackoverflow question at
http://stackoverflow.com/questions/38801955/how-does-guaranteed-copy-elision-work-in-list-initialization-in-c1z,
I was of the opinion that copy elision of a list initialization by a
copy constructor of a non-aggregate class type should not be possible
by current C++14 rules. But both GCC and Clang elide the copy
#include <iostream>
struct A {
A() { }
A(const A& ) {
std::cout << "not elided\n";
}
};
A f() { return A(); }
A g() { return {A()}; }
int main() {
f();
std::cout << "now g\n";
g();
}
None print "not elided" for the call to g(). Why is that? The elision
paragraph says
"when a temporary class object that has not been bound to a
reference (12.2) would be copied/moved to a class object with the same
cv-unqualified type, the copy/move operation can be omitted by
constructing the temporary object directly into the target of the
omitted copy/move"
This means that the copy of "A()" into a by-value parameter of a
constructor of "A" can be elided.. but not the call to the constructor
of "A" itself!? So are GCC and Clang conforming here?
in a return statement in a function with a class return type, when the
*expression* is the name of a non-volatile automatic object
A braced-init-list is not an expression. So by this rule, returning a
braced-init-list would not qualify for elision. And thus, those compilers
are eliding things which they are not allowed to elide.
Should this be considered a defect? I don't know; how often are people
going to use `return {prvalue};` where `prvalue` is the same type as the
function's return type?
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Nicol Bolas
2016-08-06 15:19:45 UTC
Permalink
Post by 'Johannes Schaub' via ISO C++ Standard - Discussion
I think you are looking somewhere wrong. Note that I was quoting C++14,
and it had another bullet which allowed elision for copies of temporaries.
That doesn't change my overall point, which is that a braced-init-list is
not a temporary. It's not a prvalue. It's not an expression of any kind.

It therefore is not subject to *any* of the rules of elision.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Loading...