C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Delete...why not a parameter by reference?!

From: Simon Schröder <dr.simon.schroeder_at_[hidden]>
Date: Sun, 24 Aug 2025 08:44:37 +0200
> On Aug 23, 2025, at 6:36 PM, organicoman via Std-Proposals <std-proposals_at_[hidden]> wrote:
>
> Really?
>
> void somefn() {
> auto ptr = std::make_unique(5);
> auto ptr2 = std::unique_ptr<int>(ptr.get()); // Maybe this should
> have been .release() ?
> }
>
> In this example you did two things:
> 1- you are breaking the smart pointer contract intentionally (that's not how we use them)
> 2- you are jumping back and forth between two worlds, smart pointers and raw pointers
>
> My proposal is meant to catch unintentional wrong usage.
> If you break an API contract you have to be careful since there is no safe guards.

You are right: If you are using smart pointers correctly, there is no problem. To this I say: If you are using raw pointer correctly, we don’t need your proposal. This example showed that it is possible to do stupid things with smart pointers as well and not just raw pointer. It is much harder, but it is possible.

BTW, the use of smart pointers does not preclude the use of raw pointers. Using smart pointers in function calls is a bad idea most of the time. Raw pointers should still be used for non-owning pointers (and you therefore never create a smart pointer from a raw pointers).
>
> If you scan through the standard library, you will find some function that deals with raw pointers directly
> std::destroy_at, std::construct_at, std::deallocate.....etc
> All these take pointers by copy, so if you fall in the following case:
> {
> m_ptr = std::allocate(...);
> //.....
> std::deallocate(m_ptr);
> //.... after some lines
> std::deallocate(m_ptr);
> }
> Because the pointer is passed by copy, nothing will tell if the resource is still alive.

Taking a pointer by reference means we would have a double indirection inside these functions. C++ has the zero overhead principle: don’t pay for what you don’t use. If I don’t want extra safety, I should not have to pay for it! You need to devise a strategy to select between safety and performance for these functions. These functions are also very low-level. This means I’d prefer performance over safety for them.

> > Let's forget the passing by reference thing....just focus on the delete operator which nullify it's pointer parameter. I don't see why it is not the standard..
>
> So throw away the part of the proposal that (if it didn't break things
> along the way) was supposed to address the truly useful part (also
> assuming that dereferencing nullptr were defined behaviour)? OK:
>
> Undefined behavior as per the standard , but segfault consensus by CPU's and microchips....
> Almost all processing units of any size, consider the address 0 and its vicinity as special memory addresses.

This is not necessarily the case for embedded processors. There might be something really important at address 0. And embedded processors also don’t necessarily have virtual memory and thus cannot produce segfaults. You make too many assumptions about processors.

Furthermore, there was a recent discussion that showed (on Compiler Explorer) that even major compilers for certain pointer types produce the value -1 for nullptr. So, nullptr is not always 0, even on the compilers you are using.
>
> If we're focusing on just having the delete operator able to set its
> argument to nullptr: it doesn't address the plethora of other copies
> of the pointer still pointing at now released memory,
>
> Setting its argument to nullptr alone has local effect only on the pointer itself, but adding the guideline 'passing the pointer by reference instead of copy to callee' will add convention to how it will live and die.
>
> The 'reference' concept, means that the referenced object ensures that it will outlive all its references.
No, it doesn’t mean that as soon as you add function calls. What it does mean is that you always are using double indirection which is bad for performance. And I’m not even sure if the compiler will always reload the pointer before every use just in case somebody else has changed it. You might have to make it volatile or something. That’s a terrible idea.
>
> Expl
> {
> // std::vector v{1,2 3} : defined outside
> auto val = v[1];
> foo_clears_vec(v);
> val = ...; // dangling ref
> }
>
> Operator[ ] returns a reference, but does not guarantee that the referenced value outlives its reference.

auto val = v[1];
creates a copy and does not store a reference even though a reference was returned. You have to write
auto &val = v[1];
And it is certainly a good idea that operator[] returns a reference because you can also store really large objects inside containers. You might even want to be able to write
v[1] = 2;
which is only possible with a reference.
A reference also means you can put non-copyable objects inside the container, like unique_ptr. You see, a reference is almost mandatory here for C++ to succeed.
>


One more additional thing: If you assign a new address to a pointer after it has been deleted and you never read from that pointer again, the compiler is allowed to optimize away this assignment. It does not matter that others have a reference to it. There is currently no legal way to write the kind of code in C++ and actually do what you want. (And disallowing this optimization will not get you any friends.) This is why this belongs into the compiler as a flag because you cannot express it in the language.

Received on 2025-08-24 06:44:52