views:

247

answers:

2

Consider this code (using CString because it's familiar and easy to see when not constructed, but nothing special about the class), tested under Visual Studio 2008:

CString DoSomething( const CString& sString )
{
    return sString;
}

CString sTest1 = DoSomething( sTest1 ); // Compiles (no warnings), fails at runtime
CString sTest2( DoSomething( sTest2 ) ); // Doesn't compile
CString sTest3; sTest3 = DoSomething( sTest3 ); // Compiles, self-assignment, works

As I understand the C++ standard, Test1 can be compiled into Test2 automatically as a compile-time optimization, provided the appropriate constructor is available (which, by default, will be generated to be identical to the first test). Notably, however, the behavior is not the same as Test3, which will work correctly.

Now I understand why Test1 does not work, and why Test2 does not compile. What I am curious about is why Test1 compiles in the first place? Is this permitted in the standard, open for interpretation, a flaw in VS2008's compiler, a deficiency in init-before-use static checking, or what? Is there any way to force the compiler to at least give a warning in this case (Test1 appears to compile clean with max warning level under VS2008)? What would be the justification for the C++ specification permitting this construct?

Edit: Alternatively, is there any way to force the compiler to compile Test1 as Test2 (and thus trigger the error)?

Edit to add verbatim error message for Test2: error C2065: 'sTest2' : undeclared identifier

+2  A: 

There have been several questions about related phenomena in the past few days.

My understanding is that even though sTest1 is not initialized, it is already a valid identifier (e.g., in C you could call sizeof on it), it just doesn't have contents. So when you make the call to DoSomething, you are passing a reference to an uninitialized variable which is legitimate but dangerous.

I am guessing that the return is the problem at runtime because you are trying to do a return-by-value (copy constructor) of what is essentially an uninitialized memory space that should have been a string. Depending on how CStrings are stored, the code is probably looking for a null terminator or for something that represents the number of allocated bytes.

Uri
That's correct on the behavior; my question is 'why'?
Nick
C++ doesn't try to prevent users from doing stupid things. One benefit of allowing this is that you could technically save a pointer to the field even if it has not yet been initialized.
Uri
On the contrary, C++ DOES try to prevent you from doing stupid things - that's one of its major advantages over C.
anon
I'm not sure I agree. It prevents you from doing some stupid things but actually gives you the opportunity to do many other stupid things.
Uri
+5  A: 

The behavior you are seeing in sTest1 is undefined in the C++ standard. It's weird and wrong but it compiles on a few compilers.

See litb's answer on the following thread for more details: http://stackoverflow.com/questions/723344/method-running-on-an-object-before-the-object-has-been-initialised/723358#723358

JaredPar
litb's answer appears to be directly on point, thanks. Any known way to get the compiler to issue a warning, or am I stuck with "try to make sure nobody writes this"?
Nick