【发布时间】:2021-05-01 15:43:08
【问题描述】:
根据[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), std::invoke(proj, lo)));
return std::invoke(comp, std::invoke(proj, v), std::invoke(proj, lo)) ? lo
: std::invoke(comp, std::invoke(proj, hi), std::invoke(proj, 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, std::__invoke(__proj, __hi), __proj_val))
return __hi;
else
return __val;
但是为了安全,我们似乎无法在第一个comp调用中使用std::forward<decltype(__proj_val)>(__proj_val)来完善转发__proj_val,也就是说我们似乎无法使用只有3个 对完美的预测实现了std::ranges::clamp。
为什么std::ranges::clamp 如此严格地限制投影数量?这是否意味着需要临时存储投影结果以满足复杂性要求?还是我对这种复杂性要求的理解有误?
【问题讨论】:
标签: c++ time-complexity c++20 std-ranges