Search code examples
c++mathconst-referencepass-by-const-reference

Should templated math functions take values or const references?


Suppose I want to implement some simple mathematical function; for example suppose it's a reimplementation of (C++17's) std::clamp: This function takes a number, a lower bound and an upper bound, and sets the number to one of those bounds if its out of the range they define. If it's a concrete numeric type, say int, I would write:

constexpr int clamp(int x, int lower_bound, int upper_bound)
{
    return x < lower_bound ? lower_bound : ( upper_bound < x ? upper_bound : x );
}

but if it's a template, I see that the sample implementation which is probably what the standard is going to have uses const&'s rather than values. So, making things simpler for quoting, something like:

template <typename T>
constexpr T clip(const T& x, const T& lower_bound, const T& upper_bound)
{
    return x < lower_bound ? lower_bound : ( upper_bound < x ? upper_bound : x );
}

My questions are:

  • Is there any benefit to taking a const reference, for T's which are simple numeric types?
  • Ditto, for types which are some abstract thing wrapping a single number as a data member (e.g. a std::chrono duration)?
  • Why is it (and is it at all) a better idea to take a const& then a value in the general case of any relatively-simple, (constexpr?), side-effect-free math-ish function?

Notes:

  • I realize taking a const& might start making sense when you have, say, some kind of k-dimensional vector type, or boost::rationals and other numeric-like types; but even then, wouldn't a compiler optimize the copying away?
  • I'm not asking about any arbitrary C++ function and whether it should just take its parameters by value, that would be a bad idea obviously.

Solution

    • Is there any benefit to taking a const reference, for T's which are simple numeric types?

    No.

    • Ditto, for types which are some abstract thing wrapping a single number as a data member (e.g. a std::chrono duration)?

    No.

    • Why is it (and is it at all) a better idea to take a const& than a value in the general case of any relatively-simple, (constexpr?), side-effect-free math-ish function?

    Imagine a bigint type that uses dynamic allocation; copying such a type is expensive.

    • Wouldn't a compiler optimize the copying away?

    Only if it can prove that copying the value have no side effect, which is hard to do unless all the code involved is visible to the compiler. (So, if your bigint uses, say, GMP, you are out of luck.)