Search code examples
c++operator-overloadingdivisionsfinaemicro-optimization

Efficient division operator for 3D vectors


I have a simple class template for 3D vectors:

template <typename T>
struct Vec3
{
    T x, y, z;  // vector components
    ...
}

where the template parameter T can be int, float or double (for now anyway). I have two requirements when overloading the division operator:

  • it must be as efficient as possible.
  • it must be available only for floating point types

I came up with this short implementation:

template <typename = std::enable_if_t<std::is_floating_point_v<T>>>
Vec3 operator/(T a) const
{
    assert(a != 0);
    T inva = static_cast<T>(1.0)/a;
    return Vec3{inva*x, inva*y, inva*z};
}

A few questions regarding this piece of code:

  • Is there another non-SFINAE way to restrict this member function to floating point types? (I am using C++17)
  • Are modern compilers smart enough to compute the division first and then perform three multiplications? Is it a waste of time and expressiveness to do that myself?
  • Is it worth using a variable template for the constant 1.0? Will the compiler be smart enough to do the static_cast at compile time? C++ static_cast runtime overhead

    template<typename T>
    constexpr T one = T(1.0);
    

EDIT: gcc didn't mind, but clang wouldn't compile the code above. This is because my usage of SFINAE is incorrect. A correct implementation would be

template <typename U = T,
          typename = std::enable_if_t<std::is_floating_point_v<U>>>
Vec3 operator/(T a) const
{
    ...
}

more details can be found here: std::enable_if to conditionally compile a member function


Solution

    • Is there another non-SFINAE way to restrict this member function to floating point types? (I am using C++17)
    • Full or partial specialization of the class (for partial specialization, it would requires changes to make class SFINAE-friendly).
    • overloads as free functions (as std::is_floating_point_v is true only for few types (float, double, long double (+ cv_variant)))

      Vec3<float> operator / (const Vec3<float>& vec, float value) {/*..*/}
      Vec3<double> operator / (const Vec3<float>& vec, float value) {/*..*/}
      Vec3<long double> operator / (const Vec3<float>& vec, float value) {/*..*/}
      

    SFINAE seems the better alternative.

    C++20 would introduce requires to discard those methods cleanly:

    Vec3 operator/(T a) const requires(std::is_floating_point_v<T>) {/*..*/}
    
    • Are modern compilers smart enough to compute the division first and then perform three multiplications? Is it a waste of time and expressiveness to do that myself?

    With floating point, result might differs, so compiler won't do that (unless it can ensure it would result in same result).

    • Is it worth using a variable template for the constant 1.0? Will the compiler be smart enough to do the static_cast at compile time?

    I will trust compiler to replace the code static_cast<float>(1.0) by 1.f.