前段时间,我想创建 Python 三元比较运算符 (
a <= b <= c
) 的等效项。在这篇文章中获得帮助后,我最终得到了一组函数:
// This helper determines if 'test' is in the range [low, high]
template<typename Low, typename Val, typename High>
bool between(Low const& low, Val const& test, High const& high);
// This helper determines if 'test' is in the range (low, high)
template<typename Low, typename Val, typename High>
bool within(Low const& low, Val const& test, High const& high);
// This helper determines if 'test' is in the range [low, high)
template<typename Low, typename Val, typename High>
bool contained(Low const& low, Val const& test, High const& high);
这些运行良好,并有各种单元测试来验证其性能。然而,最近我发现有人关闭了编译器警告,所以我重新打开它们,并在其中一个函数上收到了无符号/有符号比较警告。是的,警告是有效的,但现实是有符号/无符号比较通常是有效的,我希望能够编写可读的代码,这些代码不会被“强制类型转换”混淆,从而消除警告。 这导致尝试通过模板元编程来增强代码,以根据需要进行转换/转换,而不是其他方式。我想出了这个函数来进行一般比较,然后基于它重写了我的三元函数:
template<typename Lhs, typename Rhs>
auto cmp(Lhs const& lhs, Rhs const& rhs) -> std::strong_ordering
{
using Lhs_t = std::conditional_t<(std::is_signed<Lhs>() == std::is_signed<Rhs>()), //
decltype(lhs),
std::make_signed_t<Lhs>>;
using Rhs_t = std::conditional_t<(std::is_signed<Lhs>() == std::is_signed<Rhs>()), //
decltype(rhs),
std::make_signed_t<Rhs>>;
auto&& lhs_v = static_cast<Lhs_t>(lhs);
auto&& rhs_v = static_cast<Rhs_t>(rhs);
return lhs_v <=> rhs_v;
}
编译得很漂亮,并通过了我的(整数)单元测试。然后我更新了所有函数以使用这个新范例,但比较时出现错误
std::string
。显然,在这种情况下这不起作用,所以我认为我需要 SFINAE 并开始与
std::enable_if
战斗。我正在输掉这场战斗。我想做的是编写一个函数,如果所有参数都是整数,则将使用该函数(以消除有关有符号/无符号比较的编译器警告),并编写另一个用于所有其他类型的通用函数。
这就是我现在的位置:
template<typename Lhs, typename Rhs>
typename std::enable_if_t<std::conjunction_v<std::is_integral_v<Lhs>, std::is_integral_v<Rhs>>, std::strong_ordering>
cmp(Lhs const& lhs, Rhs const& rhs)
{
using Lhs_t = std::conditional_t<(std::is_signed<Lhs>() == std::is_signed<Rhs>()), //
decltype(lhs),
std::make_signed_t<Lhs>>;
using Rhs_t = std::conditional_t<(std::is_signed<Lhs>() == std::is_signed<Rhs>()), //
decltype(rhs),
std::make_signed_t<Rhs>>;
auto&& lhs_v = static_cast<Lhs_t>(lhs);
auto&& rhs_v = static_cast<Rhs_t>(rhs);
return lhs_v <=> rhs_v;
}
// Non-integral comparisons (e.g., strings)
template<typename Lhs, typename Rhs>
typename std::enable_if_t<std::negation_v<std::conjunction_v<std::is_integral_v<Lhs>, std::is_integral_v<Rhs>>>,
std::strong_ordering>
cmp(Lhs const& lhs, Rhs const& rhs)
{
return lhs <=> rhs;
}
编译器报告如下:
/usr/src/project/util/unit/../bool.hh:20:5: note: template argument deduction/substitution failed:
/usr/src/project/util/unit/../bool.hh: In substitution of 'template<class Lhs, class Rhs> std::enable_if_t<conjunction_v<is_integral_v<Lhs>, is_integral_v<Rhs> >, std::strong_ordering> {anonymous}::cmp(const Lhs&, const Rhs&) [with Lhs = unsigned int; Rhs = int]':
/usr/src/project/util/unit/../bool.hh:53:28: required from 'bool is_le(const Lhs&, const Rhs&) [with Lhs = unsigned int; Rhs = int]'
/usr/src/project/util/unit/bool_unittest.cpp:21:5: required from here
/usr/src/project/util/unit/../bool.hh:19:36: error: type/value mismatch at argument 1 in template parameter list for 'template<class ... _Bn> constexpr const bool std::conjunction_v<_Bn ...>'
19 | typename std::enable_if_t<std::conjunction_v<std::is_integral_v<Lhs>, std::is_integral_v<Rhs>>, std::strong_ordering>
| ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/src/project/util/unit/../bool.hh:19:36: note: expected a type, got 'std::is_integral_v<unsigned int>'
/usr/src/project/util/unit/../bool.hh:19:36: error: type/value mismatch at argument 1 in template parameter list for 'template<class ... _Bn> constexpr const bool std::conjunction_v<_Bn ...>'
/usr/src/project/util/unit/../bool.hh:19:36: note: expected a type, got 'std::is_integral_v<int>'
很明显我做的元编程是错误的,但不清楚如何修复它。
再次查看
cppreference页面模板参数B...
- 实例化
的每个模板参数Bi
必须可用作基类,并定义可转换为 boolBi::value
的成员value
这帮助我理解
std::conjunction
实际上需要谓词类,而不是它们的值。
我将声明更改为: template<typename Lhs, typename Rhs>
typename std::enable_if_t<std::conjunction_v<std::is_integral<Lhs>, std::is_integral<Rhs>>, std::strong_ordering>
cmp(Lhs const& lhs, Rhs const& rhs)
{
...
}
template<typename Lhs, typename Rhs>
typename std::enable_if_t<std::negation_v<std::conjunction<std::is_integral<Lhs>, std::is_integral<Rhs>>>,
std::strong_ordering>
cmp(Lhs const& lhs, Rhs const& rhs)
{
...
}
一切都已编译并且所有测试都通过了。
我留下这个问题,以防它可以帮助像我一样挣扎的其他人。