It is better not to bind std::clamp
return value to const ref, if one of its min
or max
parameters are rvalues.
Typical realization of std::clamp
(very simplified):
template <class T>
constexpr const T& clamp(const T& value, const T& min, const T& max)
{
return value < min ? min : max < value ? max : value;
}
And as already stated in cppreference for std::clamp there is a dangerous situation when someone write:
int n = -1;
const int& r = std::clamp(n, 0, 255);
// r is dangling
Is there any way for compile-time-detect these cases?
If you are willing to write your own clamp function, you can detect this and do something about it.
For example, suppose you want the clamp function to return by value if that is the only way to ensure that we don't get a dangling reference:
#include <type_traits>
template<class A, class B, class C>
constexpr std::conditional_t<
std::is_lvalue_reference<A &&>::value && std::is_lvalue_reference<B &&>::value && std::is_lvalue_reference<C &&>::value,
std::add_lvalue_reference_t<std::common_type_t<A, B, C>>,
std::common_type_t<A, B, C>
> clamp(A && value, B && min, C && max)
{
return value < min ? min : max < value ? max : value;
}
This would make clamp(n, 0, 255)
effectively have the signature int clamp(int&, int&&, int&&)
; you'd only get int & clamp(int&, int&, int&)
if all 3 inputs are lvalue references. Making the returned reference const
is trivial if you so desire.
You could also have the function fail to compile if they weren't all lvalue references:
#include <type_traits>
template<class A, class B, class C>
constexpr std::add_lvalue_reference_t<std::common_type_t<A, B, C>>
clamp(A && value, B && min, C && max)
{
static_assert(std::is_lvalue_reference<A &&>::value && std::is_lvalue_reference<B &&>::value && std::is_lvalue_reference<C &&>::value, "");
return value < min ? min : max < value ? max : value;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With