Лучше не связывать std::clamp
вернуть значение const ref, если один из его min
или же max
параметры являются значениями.
Типичная реализация std::clamp
(очень упрощенно):
template <class T>
constexpr const T& clamp(const T& value, const T& min, const T& max)
{
return value < min ? min : max < value ? max : value;
}
И как уже говорилось в cppreference для std :: зажим Существует опасная ситуация, когда кто-то пишет:
int n = -1;
const int& r = std::clamp(n, 0, 255);
// r is dangling
Есть ли способ обнаружить эти случаи во время компиляции?
Если вы хотите написать свою собственную функцию зажима, вы можете обнаружить это и что-то с этим сделать.
Например, предположим, что вы хотите, чтобы функция зажима возвращалась по значению, если это единственный способ гарантировать, что мы не получим висячую ссылку:
#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;
}
Это сделало бы clamp(n, 0, 255)
эффективно иметь подпись int clamp(int&, int&&, int&&)
; вы только получите int & clamp(int&, int&, int&)
если все 3 входа являются ссылками lvalue. Оформление возвращенной ссылки const
тривиально, если вы так желаете.
Вы также можете сделать так, чтобы функция не компилировалась, если бы не все ссылки на lvalue:
#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;
}
Других решений пока нет …