Const ref in C++
Before we start, although what I am going to write here is specifically about C++ (and its application in Unreal), the concept can be extended to practically any language.
I recently came across a code that went something like this:
void MethodName(const FString& param1, const float& param2, const bool& param3)
We often hear that everything that is by reference is better, after all it avoids creating a copy of the variable that was passed as an argument, and if it is “const” then much better. Is this so?
Sorry to be the bearer of bad news: no, not always.
While in the vast majority of cases it will be “better” (or the right thing to do) and while it is preferable to do everything “ref-const” and not pass by value, something that we should pass by reference or not pass as const something that we have no intention of modifying, the reality is that there are cases where this is not correct, basically because it destroys what the programmer wanted to achieve in the beginning: maximize resources.
And I’m not referring to logical situations like:
- We wanted to modify the parameter value inside the body of the method/function (const would give a compilation error)
- We wanted the modified parameter value not to modify the value of the variable originally passed as an argument (passing by reference doesn’t allow us, at least directly, that behavior).
I’m referring to situations where we didn’t want one thing or the other, and yet it’s still the least performant, that way. Why?
Let’s see… when a value is passed by reference we’re not passing the value, we’re passing a pointer that points to the variable’s memory location (after all, the reference is an alias that’s simply referencing a pointer, a memory space).
A pointer has a size that varies depending on the OS architecture, but it’s always the same for the given architecture. In a 64-bit architecture (the most common today) a pointer occupies 64 bits, which is the same as 8 bytes (it is true that there may be exceptions to the rule and that the compiler can play a role in this, but we are going to stick to the general rules).
If we stop again to look at the parameters of our method, there are 2 that I want to emphasize:
const float& param2,
const bool& param3
How much does a float occupy in C++? 4 bytes
And a bool? It is defined by the implementation (with sizeof() you can find out how much it is in yours, minimum 1 byte), but for this particular case let’s go crazy and say it’s 4 bytes.
As you can see, we are not gaining performance (in this case in memory usage), in addition to being forced to have to “deal” with a reference.
Furthermore, we can continue adding unnecessary problems (this time we do pass by reference, but it cannot be const), as explained by “The Cherno” in this part of a video.
Why not just pass by value? There is no real “why”, it would be the best practice in this case.
The same can be said for structures (not only primitives) whose member variables add up to less than 8 bytes (and do not have a copy constructor that does “weird things” or something like that).
As a general rule, I do not use references in any built-in type “below” the size of a double, nor in structures that meet the conditions mentioned. Special mention for the “out” values that I need to return from the function, clearly we need to pass them by reference (not const). But as I have already repeated several times, I do not intend to fall into all the special cases where, even with what has already been mentioned, it would be better to use a reference.
Of course, in the vast majority of cases we will be using the reference and (when appropriate) const. The idea is not to demonize the practice (and as I said, it is preferable to “err” on this side and not on the other). The idea is simply not to fall into the golden hammers and the “because I saw that someone wrote it like that it must be right”, in general I never found “formulas” that work in 100% of the cases and always reasoning what we are doing leads to better conclusions, even if it is only to save 4 bytes of memory.
Credits: Image by DCStudio on Freepik