# C++ Calculate euclidean distance between tuples, for specified indices

I wrote a small utility function that will take two tuples and returns the Euclidean distance for the given indices only:

#include <cmath>
#include <tuple>

namespace {
template <typename T> constexpr auto sq(const T& val) { return val * val; }
}

template <int... I, typename... Tp>
constexpr auto euclidean_distance(const std::tuple<Tp...>& ta, const std::tuple<Tp...>& tb) {
return static_cast<std::common_type_t<std::tuple_element_t<I, std::decay_t<decltype(ta)>>...>>(
sqrt(
(
sq(
static_cast<std::common_type_t<std::tuple_element_t<I, std::decay_t<decltype(ta)>>...>>(std::get<I>(tb)) -
static_cast<std::common_type_t<std::tuple_element_t<I, std::decay_t<decltype(ta)>>...>>(std::get<I>(ta))
) + ...
)
)
);
}

constexpr std::tuple a { (int8_t)-10, -20, (uint16_t)50, "String", 1.2f };
constexpr std::tuple b { (int8_t)20, 120, (uint16_t)150, "Another", 9.1f };

int main() {
volatile auto res_i = euclidean_distance<0,1,2>(a, b);
volatile auto res_f = euclidean_distance<0,1,2,4>(a, b);
}


Right now it only takes tuples of completely equal type, but the template logic could be adapted to use tuples that only share similar types for the used indices. I'd love to hear any comments and critiques!

• Out of curiosity, why did you make your examples volatile?
– user128454
Nov 24, 2017 at 18:35
• @Frank, my guess is that asker wanted to look at them in a debugger. I’m not sure if volatile solves vanishing variables problem though Nov 24, 2017 at 18:54
• There's some merit in looking at how implementations of std::hypot() avoid the issues of range and precision that a naive sqrt(sum(squares)) implementation suffers. It's a shame that it supports only three arguments, and not an arbitrary number, or you would then only need the element-selection logic and the arithmetic would be done. Jan 25, 2018 at 9:16

Nice work! There's not much to improve here, but still:

## Don't repeat yourself

You keep repeating the same type declaration over and over again. A simple type alias at the top of the function makes things so much cleaner:

using result_t = std::common_type_t<std::tuple_element_t<I, std::decay_t<decltype(ta)>>...>;


## A little too trigger happy on the casts

I think this would be better without the final cast. std::sqrt() returns a double when passed integral types, and that's often desirable. Letting your users cast to an integer if they want after your function returns would be more flexible.

## Prefer the namespaced version of C functions

std::sqrt is preferable over sqrt

## Anonymous namespaces in headers mean almost nothing.

Your anonymous namespace may not generate a link-time symbol, but it still pollutes the global namespace for anything that includes it. sq() would probably be better off as a lambda inside of euclidean_distance() itself, or be in an actual named namespace.

## Possible improvement:

It would be nice if this could be generalized to arbitrary norms. Passing functors that default to std::sqrt and sq would be really nice. Or perhaps a trait type that defines both the raising and the lowering operations in a single norm type might make sense.

• Actually, std::sqrt is correct and sqrt is a compiler-specific extension that can't be relied upon in portable code. Jan 25, 2018 at 9:13
• @TobySpeight Doesn't <cmath> (which op is using) include <math.h>? I was under the impression that this provides a portable version of ::sqrt().
– user128454
Jan 25, 2018 at 16:09
• No, it includes the names defined by <math.h> into the std namespace. It's allowed, but not required, to also define them in the global namespace. Conversely, <math.h> is allowed, but not required, to define names in the std namespace. Jan 25, 2018 at 18:46