Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::clamp - detect if function return value is binded to const T&

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?

like image 313
vladon Avatar asked Jan 26 '17 09:01

vladon


1 Answers

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;
}
like image 169
Justin Avatar answered Nov 17 '22 13:11

Justin