测试元素是否使用 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< 不起作用,因为它只会测试是否所有参数都大于第一个参数。
虽然没有折叠表达式可能更容易解决这个问题,但如果使用额外的辅助函数,它仍然可以解决:
#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...);
}
受到@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) && ...);
}
}
我正在寻找一种方法来检查是否使用 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< 不起作用,因为它只会测试是否所有参数都大于第一个参数。
虽然没有折叠表达式可能更容易解决这个问题,但如果使用额外的辅助函数,它仍然可以解决:
#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...);
}
受到@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) && ...);
}
}