Generic absolute value function I wanted to write a generic abs function that would correctly work for every type. Basically, I wanted to use the following algorithm:


If the type is a built-in integer, use std::abs from &lt;cstdlib&gt;.
If the type is a built-in floating-point, use std::abs from &lt;cmath&gt;.
If the type is a user-defined type with a namespace-level abs, use it.
Otherwise, call a generic abs algorithm.


Here is what I came up with:

#include &lt;cmath&gt;
#include &lt;cstdlib&gt;

namespace math
{
    namespace detail
    {
        // generic abs algorithm
        template&lt;typename T&gt;
        constexpr auto abs(const T&amp; value)
            -&gt; T
        {
            return (T{} &lt; value) ? value : -value;
        }
    }

    template&lt;typename T&gt;
    constexpr auto abs(const T&amp; value)
        -&gt; T
    {
        using std::abs;
        using detail::abs;
        return abs(value);
    }
}


The idea is to create a generic detail::abs algorithm, then to create another abs function that will choose the function to call thanks to the argument-dependant lookup. Here is what I tried to take into account:


While the generic algorithm also works for built-in integral and floating point types, std::abs may produce optimized code for these types. Using std::abs when possible will probably generate an optimized executable. That said, std::abs lacks constexpr, which is a desirable feature, and the compiler might recognize a absolute value-like construct and optimize it away...
Some types have a namespace-level abs that does not behave like the generic algorithm. Therefore, we have to call this namespace-level function if it exist.
Some types may be huge. Therefore, I chose to take the parameter by const&amp; since some namespace-level abs may also take their parameter by const&amp;.
Some types only provide operator&lt; to represent the ordering, but not the other relational operators. Therefore, calling operator&lt; in the generic algorithm is more likely to work.
I chose to use T{} instead of 0 for the comparison in the generic algorithm in order to be able to represent the default value for any given type. A type is not guaranteed to be comparable to an integer.


Here is a test case to demonstrate what the function can achieve (you can also test it online):


namespace eggs
{
    struct Foo
    {
        Foo(int val):
            val(val)
        {}

        int val;
    };

    Foo abs(Foo foo)
    {
        return { std::abs(foo.val) };
    }
}

struct Bar
{
    Bar(int val=0):
        val(val)
    {}

    Bar operator-() const
    {
        return { -val };
    }

    int val;
};

bool operator&lt;(const Bar&amp; lhs, const Bar&amp; rhs)
{
    return lhs.val &lt; rhs.val;
}

int main()
{
    using namespace std::literals;

    std::cout &lt;&lt; math::abs(-5) &lt;&lt; '\n';
    std::cout &lt;&lt; math::abs(-5.3f) &lt;&lt; '\n';
    std::cout &lt;&lt; math::abs(-5i+2.0) &lt;&lt; '\n';

    eggs::Foo foo = { -8 };
    std::cout &lt;&lt; math::abs(foo).val &lt;&lt; '\n';

    Bar bar = { -9 };
    std::cout &lt;&lt; math::abs(bar).val &lt;&lt; '\n';
}



What do you think of such a function? Did I miss any obscure error? Do you see anything that could be improved (in the implementation, I don't care about the test case)?
I believe you have a bug in your generic implementation, especially in relation to floating-point style classes that have signed-zero values:

your code:

return (T{} &lt; value) ? value : -value;


would be better as a T{} &lt;= value (or rewritten as (T{} &gt; value) ? -value : value;).

Your current logic will return -0.0 for an input value of 0.0, and that's not appropriate for an abs() function.

Additionally, I don't know how you would really test these things, because, if I am not mistaken, in floating-point comparisons with signed-zero values, -0.0 == 0.0 yet I would expect that abs(-0.0) would return 0.0.

How you resolve this issue though, I don't know.