如何解决为什么`std::ranges::clamp` 如此严格地限制了投影的数量?
根据[alg.clamp#5],std::ranges::clamp
的时间复杂度最多需要 2 次比较和 3 应用投影。 cppreference 中可能的实现如下:
struct clamp_fn {
template<class T,class Proj = std::identity,std::indirect_strict_weak_order<std::projected<const T*,Proj>> Comp = ranges::less>
constexpr const T& operator()(const T& v,const T& lo,const T& hi,Comp comp = {},Proj proj = {}) const
{
assert(!std::invoke(comp,std::invoke(proj,hi),lo)));
return std::invoke(comp,v),lo)) ? lo
: std::invoke(comp,v)) ? hi : v;
}
};
inline constexpr clamp_fn clamp;
显然不符合要求,因为它涉及3个比较和6个预测。即使我们注释掉 assert
,投影次数仍然是 4,因为 std::invoke(proj,v)
被执行了两次。
我能想到的唯一方法是暂时存储 std::invoke(proj,v)
的结果,然后将其传递给接下来的两个 comp
调用,就像 libstdc++ 一样:
auto&& __proj_val = std::__invoke(__proj,__val);
if (std::__invoke(__comp,__proj_val,std::__invoke(__proj,__lo)))
return __lo;
else if (std::__invoke(__comp,__hi),__proj_val))
return __hi;
else
return __val;
但是为了安全起见,我们似乎无法在第一个 std::forward<decltype(__proj_val)>(__proj_val)
调用中使用 __proj_val
来完美转发 comp
,这意味着我们似乎无法使用 只有 3 个预测完美实现了 std::ranges::clamp
。
为什么std::ranges::clamp
如此严格地限制了投影的数量?这是否意味着需要临时存储复杂度要求的投影结果?还是我对这种复杂性要求的理解有误?
解决方法
这是非常有意的。我在布拉格的 LWG 审查论文期间专门询问了这一复杂性要求,因为它禁止“明显”的实施。是的,这需要实现调用对值的投影,并使用 auto&&
或等效项“暂停结果在半空中”。
它还需要完美转发预计值(libstdc++ 无法做到)。这是有效的,因为 invoke
表达式需要不修改其参数(来自 regular_invocable
的要求),并且是必需的,因为 indirect_strict_weak_order
中没有任何东西需要 iter_reference_t<I1>&
的可调用性,只有 iter_reference_t<I1>
和 iter_value_t<I1>&
。
转发是有条件的移动。移动意味着“我不再需要这个值”。重新计算一个值只是为了我们可以更频繁地移动它是愚蠢的;我们的选择是创建 2 并移动两者,或者创建 1,使用它,然后在完成后移动它。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。