elided copy constructors
E. Robert Tisdale
edwin@netwood.net
Fri Mar 19 14:23:00 GMT 1999
My egcs-2.90.29 980515 (egcs-1.0.3 release) compiler
does not always elide copy constructors as permitted
by the ANSI C++ standard so I must devise a workaround
to void a costly copy of doomed objects which
won't be referenced again until they are destroyed.
The following test program illustrates the problem:
----------------------------------------------------------------------
// test.cc
#include<iostream>
class X { // a dynamic array class
// representation
int* p; // pointer to a huge array
int n; // length of the huge array
// constructors
X(int* q, int k): // private constructor definition
p(q), n(k) { }
public:
explicit
X(int k): // explicit constructor definition
p(new int[k]), n(k) { }
X(const X& x); // copy constructor declaration
~X(void) { // destructor definition
if (p) delete [] p; }
// functions
int length(void) const { return n; }
friend // salvage a doomed array
X salvage(X& x) { int* q = x.p; x.p = 0; return X(q, x.n); }
// operators
const
int& operator [](int j) const { return p[j]; }
int& operator [](int j) { return p[j]; }
};
X::X(const X& x): // copy constructor definition
p(new int[x.n]), n(x.n) {
cerr << "The copy constructor was called!" << endl;
for (int j = 0; j < n; j++) // copy the array
p[j] = x.p[j];
}
// user defined functions
#define salvage(x) x
#define return(x) return salvage(x)
inline
X f0(int n) {
cerr << "The copy constructor may be called for f0(int)." << endl;
return X(n);
}
inline
X f1(int n) {
X x(n);
for (int j = 0; j < n; j++) // Initialize x.
x[j] = j;
cerr << "The copy constructor may be called for f1(int)." << endl;
return(x);
}
inline
X g0(X x) {
cerr << "The copy constructor may be called for g0(X)." << endl;
return salvage(x);
}
inline
X g1(const X& x) {
cerr << "The copy constructor must be called for g1(const X&)." << endl;
return x;
}
int
main () {
cerr << endl
<< "The copy constructor may be called for X x0 = f0(32);" << endl;
X x0 = f0(32);
cerr << endl
<< "The copy constructor may be called for X x1 = f1(32);" << endl;
X x1 = f1(32);
cerr << endl
<< "The copy constructor may be called for X y0 = g0(f0(32));" << endl;
X y0 = g0(f0(32));
cerr << endl
<< "The copy constructor may be called for X y1 = g1(x1);" << endl;
X y1 = g1(x1);
cerr << endl
<< "The copy constructor must be called for X z0 = y0;" << endl;
X z0 = y0;
return 0;
}
----------------------------------------------------------------------
I compiled and ran `test' as follows:
----------------------------------------------------------------------
$ uname -rms
Linux 2.0.36 i686
$ g++ --version
egcs-2.90.29 980515 (egcs-1.0.3 release)
$ g++ -O2 -felide-constructors -o test test.cc
./test
The copy constructor may be called for X x0 = f0(32);
The copy constructor may be called for f0(int).
The copy constructor may be called for X x1 = f1(32);
The copy constructor may be called for f1(int).
The copy constructor was called!
The copy constructor may be called for X y0 = g0(f0(32));
The copy constructor may be called for f0(int).
The copy constructor may be called for g0(X).
The copy constructor was called!
The copy constructor may be called for X y1 = g1(x1);
The copy constructor must be called for g1(const X&).
The copy constructor was called!
The copy constructor must be called for X z0 = y0;
The copy constructor was called!
$
----------------------------------------------------------------------
After editing the `test.cc' source file
to comment out the first C preprocessor macro definition
// #define salvage(x) x
I compiled and ran `test' again as follows:
----------------------------------------------------------------------
$ g++ -O2 -felide-constructors -o test test.cc
$ ./test
The copy constructor may be called for X x0 = f0(32);
The copy constructor may be called for f0(int).
The copy constructor may be called for X x1 = f1(32);
The copy constructor may be called for f1(int).
The copy constructor may be called for X y0 = g0(f0(32));
The copy constructor may be called for f0(int).
The copy constructor may be called for g0(X).
The copy constructor may be called for X y1 = g1(x1);
The copy constructor must be called for g1(const X&).
The copy constructor was called!
The copy constructor must be called for X z0 = y0;
The copy constructor was called!
[edwin@localhost svmt]$
----------------------------------------------------------------------
Perhaps I shouldn't expect my C++ compiler to elide
the copy constructor in function g0(X) if it wasn't inline'd
but it I think that the C++ compiler should recognize
that x in function f1(int) is a reference to the return value
and initialize the return value with X(n)
instead of creating a temporary and copying it
to the return value upon return from f1(int).
My question is, "When should we expect the egcs compiler
to elide copy constructors as permitted by the standard?"
Meanwhile, I am interested in comments and suggestions
concerning workarounds.
Yes, I really do need to return by value.
No, I really don't want to do reference counting.
I am developing class libraries for application programmers
http://www.netwood.net/~edwin/svmt/
The class library need not be portable but application programmers
should be able to write portable applications without worrying
about whether copy constructors are elided or not.
Thanks in advance, E. Robert Tisdale <edwin@netwood.net>
More information about the Gcc
mailing list