Points, vectors, and normals are all different kinds of objects which should not be confused as the same. They transform differently and allow a different set of manipulations. However, if one considers an n-dimensional space, then all of them are represented by an n-tuple of real numbers after choosing an affine frame or a basis (depending on the structure of space). Their implementations are therefore almost identical, only differing on the set of operator overloads they offer. When implementing a geometric library, there are at least two ways of approaching their implementation. These are:
Implement only a Vector class, i.e., the class that allows the largest set of operations, and use this to represent also points and normals. The advantage of this approach is simplicity.
Implement a Vector, a Point, and a Normal class. The advantage of this approach is that the compiler can check for legal expressions as well as that it is possible to overload based on these types. For example, the AffineTransformation class can overload the multiplication operator differently for points, vectors, and normals, because they transform differently.
Creating separate types for vectors, points, and normals seems like a good idea. However, there are enormous maintenance issues with this approach. Consider you have to implement for all of them:
Specializations for low-dimensions 1, 2, 3, and 4.
Expression templates.
Correct constructors and operators.
Array programming support. For example: allEqual, anyLess, minIndex, etc.
Correctly working interoperability between Tuples, Points, Vectors, and Normals.
In addition consider these problems:
If you have a function to compute a mean of a Point set, it does not work to compute a mean of a Vector set.
Sometimes you want to interpret a Point as a Vector. Sometimes you want to interpret a Vector as a Point. One of these conversions needs to construct an object of another type, leading to linear complexity.
I worked with the separate-class approach for many years. However, after adding more and more useful features, the mentioned maintenance problems became too large. In addition, I have come to the conclusion that forming invalid expressions, such as adding two points, happens very rarely, and thus checking such expressions does not help much in catching bugs. While it is useful to being able to overload based on the specific type, it is rarely needed. The only case which I have encountered is when implementing a class for an affine transformation.
In the end I decided to let go of the separate-type approach and implement only the Vector class. This simplifies implementation and maintenance considerably.