In article <Is************ ****@newssvr13. news.prodigy.co m>,
Mark P <no*@my.real.em ail> wrote:
Say I have objects of class C which are fairly large. Then consider:
vector<C> vc;
vc.push_back(C( ));
Naively this would seem to construct a temporary object C(), copy it
into the space owned by the vector, and then delete the temporary object.
I realize this is implementation dependent, but will most modern
compilers optimize away the creation of the temporary object, or is
there a compelling reason why this is not possible?
Thanks,
Mark
In addition to some of the fine answers you've already received:
In general the compiler does not know where (at what address) the vector
is going to put the C() being push_back'd. During the push_back
execution, the vector will decide where the object needs to be
constructed (maybe space needs to be allocated, maybe not). At this
point the temporary C() will have long since been constructed in order
to initiate push_back execution in the first place.
So no, the copy can not be optimized away today (or at least is not
optimized away in practice today).
All that being said, there is a proposal before the committee to address
this problem: move semantics / rvalue reference:
http://www.open-std.org/jtc1/sc22/wg...2002/n1377.htm http://www.open-std.org/jtc1/sc22/wg...004/n1690.html http://www.open-std.org/jtc1/sc22/wg...005/n1770.html http://www.open-std.org/jtc1/sc22/wg...005/n1771.html
The basic idea is that the class C can have a move constructor which can
execute far more efficiently than a copy constructor by transferring
resources from the source to the target (instead of duplicating
resources). And you can safely move from temporaries with copy syntax
because nobody else has a reference to the temporary in order to detect
a difference.
So expanding on Robert's example:
#include <iostream>
#include <vector>
class C {
public:
// constructors
C() {
std::cerr << "C::C()" << std::endl;
}
// copy semantics
C(const C&) {
std::cerr << "C::C(const C&)" << std::endl;
}
C& operator=(const C&) {
std::cerr << "C::operator=(c onst C&)" << std::endl;
return *this;
}
// move semantics
C(C&&) {
std::cerr << "C::C(C&&)" << std::endl;
}
C& operator=(C&&) {
std::cerr << "C::operator=(C &&)" << std::endl;
return *this;
}
// destructor
~C(void) {
std::cerr << "C::~C(void )" << std::endl;
}
};
int main() {
std::vector<C> vc;
vc.push_back(C( ));
return 0;
}
This will now print out:
C::C()
C::C(C&&)
C::~C(void)
C::~C(void)
I.e. The temporary is still created, but it is move constructed from
instead of copy constructed from to create the C internal to the vector.
In this simple example there is no efficiency gain. However if C held
resources (such as dynamic memory, file references, mutex locks, socket
connections, etc.) then those resources can be transfered out of the
temporary and into the vector by coding C(C&&) appropriately. ~C() will
then be called on the temporary which must then be able to properly deal
with a resource-less object.
vector will also use move semantics instead of copy semantics when
moving around elements internally (e.g. during buffer reallocation).
The status of this proposal within the committee is currently promising,
and Metrowerks has a full implementation of it in a not yet released
compiler/lib.
-Howard