# Templated Point class of any dimension

I needed to make a point class for a term project that involved arbitrary dimensional points. I came up with this, and I know that it works fine, but I'm looking to improve it so I can reuse it in the future. Any criticisms or suggestions are very much appreciated.

#ifndef POINT_HPP
#define POINT_HPP

#include <array>
#include <cmath>
#include <initializer_list>
#include <iostream>

template <size_t D>
struct Point;

template <size_t D>
struct Point {
Point(const std::initializer_list<double>& v) {
size_t i = 0;
for (auto e : v) {
coordinates_[i++] = e;
}
}

explicit Point(const std::array<double, D>& arr) : coordinates_{arr} {}

double operator[](size_t dimension) const { return coordinates_[dimension]; }
Point<D>& operator=(const Point& other) {
this->coordinates_ = other.coordinates_;
return *this;
}
std::array<double, D> coordinates_;
};

/*
* Finds euclidian distance between two Point classes of same dimension
*/
template <size_t D>
double distance(const Point<D>& lhs, const Point<D>& rhs) {
double dist = 0;
for (size_t i = 0; i < D; i++) dist += std::pow(lhs[i] - rhs[i], 2);
return std::sqrt(dist);
}

/*
* Finds euclidian distance between Point and axis
*/
template <size_t D>
double distance(const Point<D>& point, double value, int axis) {
return std::abs(point[axis] - value);
}

/*
* Print the point components of a Point in ascending order.
* Prints in the form {x, y, z} with no newline.
*/
template <size_t D>
std::ostream& operator<<(std::ostream& os, const Point<D>& point) {
os << "{";
for (size_t i = 0; i < D - 1; i++) {
os << point[i] << ", ";
}
os << point[D - 1] << "}";
return os;
}

template <size_t D>
bool operator==(const Point<D>& lhs, const Point<D>& rhs) {
return lhs.coordinates_ == rhs.coordinates_;
}

namespace std {
/*
* Make our Point class hashable
*/
template <size_t D>
struct hash<Point<D>> {
size_t operator()(const Point<D>& x) const {
return hash<array<double, D>>()(x.coordinates_);
}
};

/*
* Also make it swappable for sorting
*/
template <size_t D>
void swap(Point<D>& lhs, Point<D>& rhs) {
swap(lhs.coordinates_, rhs.coordinates_);
}
}  // namespace std

#endif  // POINT_HPP

• Code updated based on suggestions here Dec 20, 2016 at 4:37

Overall, this seems to be a small but useful template. However, there are a few things that I think might be improved.

## Sorting requires more than swap

While the provided swap function would assist in some kind of sorting, without a comparison operator, we still can't actually sort. This may well be by design, in which case a comment in the code pointing this out would be helpful.

## Consider default constructor

There is no simple way at the moment to create a generic templated origin point (all coordinates zero). This makes it a bit more difficult to create something like a distance_from_origin function that is generic.

## Consider projection operator

It's often useful to project, say, a 3D point onto a 2D canvas. This could also likely be made generic.

## Do a range check

The distance function that measures from an axis does no range checking on the axis number and there does not seem to be a good generic way to add one. Consider providing a range checking version.

• As for the sorting-point: how would you sort 2D-points or higher dimensional points? It depends upon how you want them sorted. IMO this would rather belong to a collection of utility methods than to the struct itself, if anywhere else than the actual application.
– user82687
Dec 19, 2016 at 22:36
• @Paul indeed there is no obvious single way to sort such points, but the existing code provides for a swap method for sorting. It seemed to me that it was worth pointing out that more than just a swap would be required for sorting. Dec 19, 2016 at 22:45
• @Paul: I'm using this point class to construct k-d trees, hence the swap function. The tree is sorted based on an axis at each level, which is why I had the distance with the axis function. Dec 20, 2016 at 3:08

Very good. Yet making it more generic is not hard:

template<typename T, size_t D>
class PointExt {
public:

PointExt(std::initializer_list<T> v) {
std::copy(v.begin(), v.end(), coordinates_.begin());
}

explicit PointExt(const std::array<T, D>& arr) : coordinates_{arr} {}

T operator[](size_t dimension) { return coordinates_[dimension]; }
const T operator[](size_t dimension) const { return coordinates_[dimension]; }

private:
std::array<T, D> coordinates_;
};


Now you can compute, say, a Manhattan distance of two points with different component types (by returning the distance in the most precise type):

template<typename T1, typename T2, size_t D>
decltype(T1{} + T2{}) manhattan_distance(const PointExt<T1, D>& lhs,
const PointExt<T2, D>& rhs) {
auto sum = decltype(T1{} + T2{}){};

for (size_t i = 0; i != D; ++i) {
sum += std::abs(lhs[i] - rhs[i]);
}

return sum;
}


In the output overload, you can write one line more succintly:

template<typename T, size_t D>
std::ostream& operator<<(std::ostream& os, const PointExt<T, D>& point) {
os << "(";
for (size_t i = 0; i < D - 1; ++i) {
os << point[i] << ", ";
}
return os << point[D - 1] << ")";
}


Hope that helps.

• Nice, I like the initializer list constructor a lot Dec 20, 2016 at 3:14