I have a bunch of functions that check for collisions between various types of shape.
bool Collides(Rect r, Circle c);
bool Collides(Rect r, Line l);
bool Collides(Line l, Circle c);
I was hoping I could implement a single templated function that could halve my implementation count by allowing it to swap the input parameters. So that instead of also having to implement:
// The same as before but the input parameters swapped
bool Collides(Circle c, Rect r) { return Collides(r, c); }
bool Collides(Line l, Rect r) { return Collides(r, l); }
bool Collides(Circle c, Line l) { return Collides(l, c); }
I could instead write this once:
template <typename Shape1, typename Shape2>
bool Collides(Shape1 a, Shape2 b)
{
return Collides(b, a);
}
Unfortunately when both Collides(a, b)
an Collides(b, a)
are unimplemented it calls the templated function recursively at runtime, which is obviously unintended behaviour.
Is there some C++ tag or feature that allows you to turn off or disallow argument type deduction for a specified line or block? The intent would be to force the compiler to look for a non-templated implementation then fail to compile if one didn't exist.
One time that a function template is not looked up is during the declaration of the function (Before the opening {
). Taking advantage of that, we can SFINAE unimplemented arguments out:
template<typename Shape1, typename Shape2>
auto Collides(Shape1 a, Shape2 b) -> decltype(::Collides(b, a)) {
return Collides(b, a);
}
Though note that this must be written after all the other declarations of Collides
.
You can also just call a different function that delegates:
template<typename Shape1, typename Shape2>
auto ActualCollides(Shape1 a, Shape2 b) -> decltype(Collides(a, b)) {
return Collides(a, b);
}
template<typename Shape1, typename Shape2>
auto ActualCollides(Shape1 a, Shape2 b) -> decltype(Collides(b, a)) {
return Collides(b, a);
}
// Or rename `Collides` into `CollidesImpl` and you can call this `Collides` instead
Which will take into account future Collides
functions because of ADL.
There's no way to remove function templates from the overload set. In your particular case there are workarounds, such as:
struct CollidesImpl {
bool operator()(Rect r, Circle c);
bool operator()(Rect r, Line l);
bool operator()(Line l, Circle c);
};
template <typename Shape1, typename Shape2>
bool Collides(Shape1 a, Shape2 b)
{
static_assert(std::is_invocable_v<CollidesImpl, Shape1, Shape2> ||
std::is_invocable_v<CollidesImpl, Shape2, Shape1>,
"No implementation exists for these argument types");
if constexpr(std::is_invocable_v<CollidesImpl, Shape1, Shape2>) {
return CollidesImpl{}(a, b);
} else {
return CollidesImpl{}(b, a);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With