3

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

Touloudou
  • 2,079
  • 1
  • 17
  • 28
  • "smart enough" yes, but only if you let them by using `-ffast-math`, unless the divisor is known to have a reciprocal that's exactly representable as a `T`. e.g. yes for compile-time-constant powers of 2, like `2.0` and `0.5`, because binary floating point can represent such fractions. Introducing an inexact reciprocal is not allowed by ISO C++ / IEEE-754 rules. – Peter Cordes Aug 27 '19 at 09:48

1 Answers1

3
  • 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.

Jarod42
  • 203,559
  • 14
  • 181
  • 302