3 ms·
Having everything be const references is in a sense optimal, but is so prone to misuse that I wouldn't want it in the standard library. One option is to pass e
by pbsd 5y ago
Having everything be const references is in a sense optimal, but is so prone to misuse that I wouldn't want it in the standard library.
One option is to pass everything by value. Move semantics does the rest. Like:
template<typename T>
T clamp(T v, T lo, T hi) {
if(v < lo) v = std::move(lo);
if(v > hi) v = std::move(hi);
return std::move(v);
}
For fundamental types, this is the same or better than before (including __int128_t and such). For bigint types with heap allocation, i.e., GMP wrappers and such, moving is fairly inexpensive (but for constant lo, hi, they need to be created/copied each time...). For fixed-width bigint types, moving is no different from copying and the cost is higher. In any case, the 99-percentile usage of these functions is with fundamental types, where pass-by-value is clearly the better choice.
Alternatively, you could have
template<typename T>
T& clamp(T& v, T const& lo, T const& hi) {
if(v < lo) v = lo;
if(v > hi) v = hi;
return v;
}
This forces the returned reference to be an l-value, and so returning dangling temporaries becomes more difficult. There's the added cost for the assignments, though.
- sesuximo 5y agoMoving into clamp is problematic since you’d make clamp destructive. You couldn’t use any of the arguments after clamp. Non const refs seems like a weird idea (what if I got a const ref from my callee? Why can’t I clamp?).
- pbsd 5y agoTrue, but it's entirely voluntary: - clamp(a, b, c), where a, b, c are all l-values would copy everything. Not destructive by default (but wasteful on "big" types). - clamp(a, std::move(b), c) would potentially destroy b, but that's something the caller explicitly opted into. - clamp(a, T{ something }, T{ something else }) would be an implicit move, but nobody is going to accidentally use those r-values anywhere else. The non-const ref version returning is not something I quite like either; it would make more sense to me to have `void clamp(T& x, T const& l, T const& h)`, where the semantics would be more like `x.clamp(l, h);`. But to retain the same API I returned the reference.