Object to reference conversion (C4239)

Started by
16 comments, last by Kest 17 years, 6 months ago
Quote:Original post by Kest
I just cranked my compiler up from level 3 to level 4 warnings, and there's a lot of work here. One of the warnings pertain to something that I've really relied on:

warning C4239: nonstandard extension used : 'argument' : conversion from 'Class' to 'Class &'.


This is indeed a non-standard extension - by the standard, writing the code this way (without const-correctness) is an *error*. Strange that you'd only get a warning at level 4 for that. :
The philosophy is that if you create a temporary, then you are saying you don't care about it - and don't have a way to check any changes to the object - which is mutually incompatible with the idea of passing it to a function that is expected to create such changes. If the function *does* modify the value, you can always set aside a variable for it separately. Sometimes it helps to make an anonymous scope for the "temporaries" plus the call. But otherwise, FFS be const-correct :)
Advertisement
Quote:Original post by Zahlman
The philosophy is that if you create a temporary, then you are saying you don't care about it - and don't have a way to check any changes to the object - which is mutually incompatible with the idea of passing it to a function that is expected to create such changes.

The plug object was specifically made for that purpose. It translates different types of data so that the loading code doesn't need to worry about where it's coming from. It's literally made to be declared as a temporary to be sent to 'load' functions. It needs to modify it's own data to handle the memory reading, so passing as const won't cut it. Are there any functional differences between declaring the temporary as a parameter as opposed to declaring it before the function call?

void Function(Object &obj, int yep, int nope);...{ return Function( Object(1), 2, 3 ); }{ Object obj(1); return Function( obj, 2, 3); }


Quote:If the function *does* modify the value, you can always set aside a variable for it separately.

I'm pretty sure I've misunderstood the meaning behind this statement. Do you mean to send temporary data variables to the object to work with from external sources just to avoid sending the reference as non-const?

Quote:Sometimes it helps to make an anonymous scope for the "temporaries" plus the call. But otherwise, FFS be const-correct :)

I'm not sure I follow you. FFS, hacking code to warp objects into working as const isn't going to solve any of my problems :)
Quote:
Quote:If the function *does* modify the value, you can always set aside a variable for it separately.



I'm pretty sure I've misunderstood the meaning behind this statement. Do you mean to send temporary data variables to the object to work with from external sources just to avoid sending the reference as non-const?

Quote:Sometimes it helps to make an anonymous scope for the "temporaries" plus the call. But otherwise, FFS be const-correct :)



I'm not sure I follow you. FFS, hacking code to warp objects into working as const isn't going to solve any of my problems :)


what i think zahlman means is whenever you want to call the function to this

{    // some code    // then you want to call it here so you do this    {        SomeClass temp(0, 5, 1); // create the temp        SomeFunction(temp);    }    // now temp doesnt exits here so you arnt poluting this scope with heaps    // of temp variables    // some more code here}
I understand the anonymous scope part of it, but what is the reason? How does that solve any of the problems? The inlined functions which declare the temporary variable already exist. All I had to do was move the construction call out of the parameters and give it a name. I would have done that from the start, I just didn't realize there was any difference between those two situations until I encountered this warning.

The fact that the data isn't destructed right after the call was never a concern for me.
Quote:I understand the anonymous scope part of it, but what is the reason?


There are three main reasons that you would do it.

First the memory being freed which you said you dont care about so ill skip that.

Secondly the variable names if you dont use the anonymous scope and you only want to pass the one class/type like this then you can just have one variable called temp, but suppose you have three different classes that you have to do this with and you need to use all of them in the one scope, you would then need one variable per class in that scope.

And that brings me to the third and MAIN REASON that you would do it, readability, using one variable for the passing of temporaries to mulitple functions could and probaly will confuse ppl who read your code. In contrast using the anonymous scope makes it obvious to anyone reading that the temp variable will ONLY be used to call the function and NOTHING else NOT EVEN other calls to the SAME or a different function.

Quote:How does that solve any of the problems? The inlined functions which declare the temporary variable already exist. All I had to do was move the construction call out of the parameters and give it a name.


doing explicatly what the copiler was doing for you implicatly will get rid of the warning :-)

Edit: typo
Quote:Original post by Kest
Quote:Original post by Conner McCloud
Quote:Original post by Kest
Is it safe to ignore this warning?

It is rather rare that you want to ignore any warnings. If they could be ignored, the compiler probably wouldn't bother warning you about them.

CM

As far as I understand it, the functionality itself is safe, isn't it? The object becomes invalid once the function call returns. It's impossible to access it afterwards, because it has no handle.

In this specific case, yeah, the functionality probably is safe. That's why its just a warning. However, that's not to say it will always be safe. Perhaps tomorrow he will compile on gcc, and gcc performs some optimization that renders all that perfectly safe code into a series of access violations. Or maybe the day after that he does this:
void func(string& str, int& val);int main(){   func(string("Hello thar"), 5);}

The first parameter will compile cleanly, but the second one will spit out an error. The exact same error that the first parameter is supposed to raise. That is, at best, a head scratcher.

*edit: I just realized "he" and "you" are the same person. Oops.

CM
There's a perfectly reasonable solution to all this that doesn't require elaborate scoping: don't use a reference in the first place. Pass by value, so that you get a "plug" that you can modify at will. Your internal functions can accept non-const references if you want, because you won't be passing around a temporary any more.

CM
Quote:Original post by Conner McCloud
There's a perfectly reasonable solution to all this that doesn't require elaborate scoping: don't use a reference in the first place. Pass by value, so that you get a "plug" that you can modify at will.

I can't create a copy. The object will open and close files on construction and destruction when in file mode. The object is used to load game state data in. A copy would need to be created every time the object is passed between game hierarchy objects. The only way to avoid that would be to code complicated reference counts or "I know I'm a copy" recognition.

I call Region->Load( Plug( filename ) ); to load a normal game
Region calls Maps->Load( plug ); and Load( plug ) for any other internal data
Map calls Objects->Load ( plug ); for all of it's objects
Objects call Data->Load( plug ); for whatever they need to load

The plug keeps offsets and internal data pointers during this whole trip. With that, I'm able to save and load a complicated hierarchy of objects with little effort. And the data can be written/loaded to memory, file, compressed datafile, or whatever else I add to the plug object later.

The only way for it to stay functional is to declare it before the function call and send it as a non-const reference. Seems like a perfectly valid solution to me.

Quote:Your internal functions can accept non-const references if you want, because you won't be passing around a temporary any more.

I know it's probably obvious by now, but I wasn't passing by non-const just because I want. That's the only way the system can work. [smile]

This topic is closed to new replies.

Advertisement