in C++, const is much more flexible than in Delphi, more so than that documentation explains. The semantics of const depend on WHERE the const is placed. In general, const applies to the thing on its left, unless there is nothing there then it applies to the thing on its right instead.
So, for instance, if we have these declarations in C++:
void FreeAndNil(const TObject* &Obj);
or
void FreeAndNil(TObject const * &Obj);
Then the const applies only to the TObject instance that is being pointed at, so its data members cannot be changed by the function. The const does not apply to the TObject* pointer itself, which is being passed by reference, so that pointer can be freely assigned a new value by the function to point at a different TObject instance or null, and that change is reflected to the caller's variable.
Whereas if we have this declaration instead in C++:
void FreeAndNil(TObject* const &Obj);
Then the const applies only to the TObject* pointer itself, thus it can't be assigned a new value by the function. The const does not apply to the TObject instance, so its data members can be freely modified by the function.
The two examples above can be combined to have a const pointer to a const TObject instance:
void FreeAndNil(const TObject* const &Obj);
or
void FreeAndNil(TObject const * const &Obj);
It does not make sense to put const after Obj, since by definition a reference cannot be reseated once it has been initialized:
void FreeAndNil(... &Obj const);
or
void FreeAndNil(... &Obj const);
Now, given this declaration in Delphi:
procedure FreeAndNil(const [ref] Obj: TObject);
The semantics of this are roughly equivalent to the above C++ code - a reference to a const pointer to a const TObject instance (I don't have 10.4 installed to look at how FreeAndNil() is actually declared in C++Builder). That is fine and good to get a pointer to a TObject-based instance passed into the function by reference, but normally Delphi would then not allow that pointer to be assigned a new value. So FreeAndNil() employs a workaround for that:
TObject(Pointer(@Obj)^) := nil;
Applying the @ operator to a reference parameter returns the address of the passed variable. Then all const-ness on that variable is being casted away, thus allowing the function to assign a new value to that variable.
That would be something along the lines of the following in C++:
((TObject*&)(*(void**)&ref)) = nullptr;
or
reinterpret_cast<TObject*&>(const_cast<void*&>(*reinterpret_cast<const void * const *>(&ref))) = nullptr;
or
const_cast<TObject*&>(const_cast<const TObject * const &>(ref)) = nullptr;
Except that if a C++ compiler sees a 'const TObject* const &' function parameter, and is passed anything other than a 'const TObject*' variable, an implicit conversion is performed and an rvalue gets passed to the reference parameter, so the function modifies the rvalue, not the caller's original variable. For example: https://ideone.com/CO4Dac So, there is likely some additional compiler magic in C++Builder to avoid this problem with FreeAndNil() specifically. Unless I'm missing something.