I've been reading about how Copy ellision and return value optimization can improve speed by avoiding calls to an object copy constructor. I understand how the mechanisms work, but I wondered if this could not lead to programs that don't behave as one would expect.
In essence, my question is; what happens if we write the copy constructor to not create an object that is a copy of another object? In other words, what if
AClass original;
AClass copy ( original );
// copy == original -> false
Say, for example, we had a class like so:
// AClass.hpp
struct AClass
{
static int copyCount;
int copyNumber;
AClass():copyNumber(0){}
AClass( AClass const& original ):copyNumber(++copyCount){} // I think this is the signature for the copy constructor
};
// AClass.cpp
int AClass::count ( 0 );
This is obviously terrible behavior to put into a class, I'm not saying I'd do anything like this. The point stands, however; what if we relied on the side-effects of the copy? In this example, keeping track of how many copies we've made. I would expect that an optimization should not affect how the program runs. However, copy elision could cause the following code to fail:
// Main.cpp
AClass MakeAClass()
{
return AClass();
}
int main()
{
AClass copy ( MakeAClass() );
if ( AClass::copyCount == 1 )
{
return 0;
}
else
{
return -1;
}
}
This could return 0 when I build in debug mode with no optimizations, but suddenly fail when I turn on optimizations and the return from MakeAClass is placed directly on copy, skipping the copy constructor.
Is there a check when the compiler attempts these optimizations to look for side-effects? Is it wrong to expect the code to perform a copy when you ask for a copy?