测试元素是否使用 C++17 折叠表达式排序

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

我正在寻找一种方法来检查是否使用 c++ 折叠表达式对参数进行了排序。 天真的方法:

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

不起作用,因为它扩展为 ((arg0 < arg1) < arg2) < ...,最终将 bool 与类型名称 Args 进行比较(在编译时使用例如 int 时可能很危险,除非您提高警告级别)。

挑出列表中第一个参数的技巧适用于检查所有元素是否相等:

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

但它在这里对 operator< 不起作用,因为它只会测试是否所有参数都大于第一个参数。

虽然没有折叠表达式可能更容易解决这个问题,但如果使用额外的辅助函数,它仍然可以解决:

Try it online!

#include <tuple>
#include <type_traits>
#include <iostream>
#include <iomanip>

template <typename ... Args, std::size_t Index0, std::size_t ... Indices>
bool helper(std::tuple<Args...> const & args,
       std::index_sequence<Index0, Indices...> const &) {
    return ((std::get<Indices - 1>(args) <
             std::get<Indices>(args)) && ...);
}

template <typename ... Args>
bool are_strictly_increasing(Args const & ... args) {
    return helper(std::tie(args...), std::index_sequence_for<Args...>{});
}

int main() {
    std::cout << std::boolalpha
        << are_strictly_increasing(false, true, 2, 3) << std::endl
        << are_strictly_increasing(3, 2, 1) << std::endl;
}

输出:

true
false

在我看来,如果你的第一个论点无论如何都是孤立的更好

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

假设参数有一个通用类型

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

并给定两个普通类型的变量,一个用第一个参数初始化 a0

CT c0, c1 = a0;

你的折叠表达式可以是

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

如果不隔离第一个参数,就会出现 c1 初始化的问题。您可以尝试使用 std::numeric_limits<CT>::min(),但如果 args... 的第一个值等于此值(当您检查无符号值时这并非不可能),代码将失败。

下面是一个完整的编译示例

#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;
}

折叠表达式很酷,但在这种情况下最好放弃它们并使用带有递归的旧解决方案:

template <typename Arg1, typename Arg2>
bool are_strictly_increasing(Arg1 const& a, Arg2 const& b)
{
    return a < b;
}

template <typename Arg1, typename Arg2, typename... Args>
bool are_strictly_increasing(Arg1 const& a, Arg2 const& b, Args const&... args)
{
    return (a < b) && are_strictly_increasing(b, args...);
}

https://godbolt.org/z/3E58P5n46

受到@max66 的回答的启发,我进行了避免复制的改进。
由于在 ((*arg < args ? (arg = &args, true) : false)

中使用了带有嵌套逗号运算符的三元运算符,它使用了单个辅助变量
template <typename Arg, typename... Args>
bool are_strictly_increasing(Arg const& arg0, Args const&... args) {

  if constexpr (sizeof...(args) == 0) {
    (void) arg0; // avoid unreferenced formal parameter compiler warning
    return true;
  } else {
    Arg const* arg = &arg0;
    return ((*arg < args ? (arg = &args, true) : false) && ...);
  }
}