Search code examples
c++c++17fold-expression

Test if elements are sorted with C++17 fold-expression


I am looking for a way to check that arguments are sorted using a c++ fold expression. The naive approach:

template <typename... Args>
bool are_strictly_increasing(Args const&... args) {
    return (args < ...);
}

Does not work as it expands to ((arg0 < arg1) < arg2) < ... which ends up comparing bool to whatever the typename Args is (that can be treacherous when using e.g. int as it compiles unless you crank up warning levels).

The trick to single out the first argument in the list works for checking all elements are equal:

template <typename Arg, typename... Args>
bool are_all_equal(Arg const& arg0, Args const&... args) {
    return ((arg0 == args) && ... );
}

but it won't work here for operator< since it would only test whether all arguments are greater than the first one.


Solution

  • It seems to me that is better if your first argument is isolated anyway

    template <typename A0, typename... Args>
    constexpr bool are_strictly_increasing(A0 const & a0, Args const&... args)
    

    Supposing there is a common type for the arguments

    using CT = std::common_type_t<A0, Args...>;
    

    and given two variable of common type, one initialized with the first argument a0

    CT c0, c1 = a0;
    

    your fold expression can be

    return ((c0 = c1, c0 < (c1 = static_cast<CT>(args))) && ...);
    

    Without isolating the first argument, there is the problem of the inizialization of c1. You can try with std::numeric_limits<CT>::min(), but the code fail if the first value of args... is equal to this value (that isn't improbable when you check unsigned values).

    The following is a full compiling example

    #include <iostream>
    
    template <typename A0, typename... Args>
    constexpr bool are_strictly_increasing(A0 const & a0, Args const&... args)
    {
      using CT = std::common_type_t<A0, Args...>;
    
      CT c0, c1 = a0;
    
      return ((c0 = c1, c0 < (c1 = static_cast<CT>(args))) && ...);
    }
    
    int main()
    {
      std::cout << are_strictly_increasing(0, 1, 2, 3, 4) << std::endl;
      std::cout << are_strictly_increasing(0, short{1}, 2l, 3u, 4ull) << std::endl;
      std::cout << are_strictly_increasing(0, 1, 2, 3, 4, 4) << std::endl;
      std::cout << are_strictly_increasing(0, 1, 21, 3, 4, 4) << std::endl;
    }