C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Safer API for minmax with friends?

From: Jarrad Waterloo <descender76_at_[hidden]>
Date: Thu, 6 Mar 2025 08:34:31 -0500
#1

The proactive defensive programming kindness of API authors to those who
use their API(s).

When a cont& return is dependent upon a const& input parameter, in part or
in whole, the input parameter can be changed to a const reference_wrapper
of const which prevents one from using temporaries.

const T& min( const reference_wrapper<const T> a, const
reference_wrapper<const T> b );

#2

Fix the language with temporary reduction.

2023
variable scope
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2730r1.html
C Dangling Reduction
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2750r2.html
Reference checking
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2878r6.html
2022
temporary storage class specifiers
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2658r1.html

For those of you who have sacrificed the safety of the 99% for the 1% of
temporary locks, consider this, those classes that need short lived
temporary semantics could be annotated with [[temporary]]; problem solved.

#3

The problem is worse and more embarrassing than you know. What about when
developers actually pass in constants to const parameters.

```c++
auto [min, max] = std::minmax(37, 30 - 7);
```

These dangle in the same way as before even though the compiler is more
than capable of putting 30 and 30-7 into ROM. Since there are little to no
ROM or const + static guarantees in the language than even this could,
which should always work, fails and worse perplexes beginners.

2024
const references to constexpr variables
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p3218r0.html
2023
constant dangling
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2724r1.html
2022
implicit constant initialization
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2623r2.html

The currently broken examples could be fixed with these proposals but for
now you may have to liberally use some, yet to be accepted, C++26 features
such as the following.

2024
std::constexpr_wrapper
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p2781r5.html

```c++
auto [min, max] = std::minmax(std::cw<a>, std::cw<b - a>);// but only if a,
b-a are constant expressions and there type is structural otherwise the
CONSTANT macro works regardless
```

2025
define_static_{string,object,array}
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p3491r1.html

```c++
auto [min, max] = std::minmax( *define_static_object(a),
*define_static_object(b - a));// but only if a, b are structural otherwise
the CONSTANT macro works regardless
```

By the way those are explicit constant initialization. For the simpler
implicit constant initialization see the following.

Non-transient allocation with vector and basic_string
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p3554r0.html
There are many other recent proposals as well that would allow the original
syntax.
```c++
auto [min, max] = std::minmax(a, b - a);
```

Summary i.e. the key take aways
-------------
1) These simple solutions are easier than Rust because they don't require a
noisy borrow collector klaxon warning. The compiler just makes it work with
the knowledge it already knows.
2) implicit and explicit constant initialization does not break existing
code and can be contributed back to C to make our shared community safer;
it also makes our language simpler
3) fixing temporaries does not break existing code in C and depending upon
the approach any C++ breakage can be mitigated significantly
4) These are low hanging fruit that can reduce the use after free of the
stack which the Rust community has been pointing out even more than range
access errors for the past decade. It is also the hardest memory bug for
programmers because we have so few mitigations at present because it
requires compiler support.

...

NOTE: The opposite problem has been raised by the following.

favor ease of use
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p3326r0.html



On Thu, Mar 6, 2025 at 5:27 AM Robin Savonen Söderholm via Std-Proposals <
std-proposals_at_[hidden]> wrote:

> Hi!
>
> I recently got burned by a piece of innocent looking code that was
> something along the lines of this:
> ```c++
> auto [min, max] = std::minmax(a, b - a);
> ```
> The problem stems from the fact that std::minmax (not initialiser_list
> api) returns a std::pair<T const&, T const&> regardless of what the
> arguments are AND that the structural bindings 'auto' is just making sure
> that the object being separated is not a reference, NOT that the objects
> inside [...] are not references...
> In the best of worlds I would argue that `auto [x, y] = ...` would mean
> that `x` and `y` are pure values, but I guess such a change could cause
> trouble due to breaking currently working code that relies on the reference
> capture (and it would complicate the meaning of `auto& [x, y] = ...`).
> A simpler option would be to change the API for the minmax function (and
> perhaps other, similar looking functions) to something where if an RValue
> is detected amongst the arguments, it would return a std::pair<T, T> only
> (or pair<T const, T const>), while if all arguments are lvalue references
> it could keep it's current form (for speed or for the need of testing the
> address of an argument or something..)
>
> What would be the best remedy for things like this you think?
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>

Received on 2025-03-06 13:34:43