Вызовите функцию, которая не является шаблонной - PullRequest
4 голосов
/ 28 апреля 2020

У меня есть набор функций, которые проверяют наличие коллизий между различными типами фигур.

bool Collides(Rect r, Circle c);
bool Collides(Rect r, Line l);
bool Collides(Line l, Circle c);

Я надеялся, что смогу реализовать одну шаблонную функцию, которая могла бы вдвое уменьшить количество моих реализаций, позволив ей поменять местами входные параметры. Так что вместо того, чтобы также реализовать:

// 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); }

, я мог бы вместо этого написать это один раз:

template <typename Shape1, typename Shape2>
bool Collides(Shape1 a, Shape2 b)
{
    return Collides(b, a);
}

К сожалению, когда оба Collides(a, b) и Collides(b, a) не реализованы, он вызывает шаблонный рекурсивная функция во время выполнения, что, очевидно, является непреднамеренным поведением.

Существует ли какой-либо тег C ++ или функция, позволяющая отключить или запретить вывод типа аргумента для указанной строки или блока? Цель состоит в том, чтобы заставить компилятор искать не шаблонную реализацию, а затем не скомпилировать, если она не существует.

Ответы [ 2 ]

6 голосов
/ 28 апреля 2020

Один раз, когда шаблон функции не ищется, во время объявления функции (перед открытием {). Воспользовавшись этим, мы можем вывести SFINAE неосуществленные аргументы:

template<typename Shape1, typename Shape2>
auto Collides(Shape1 a, Shape2 b) -> decltype(::Collides(b, a)) {
    return Collides(b, a);
}

Хотя учтите, что это должно быть написано после всех других объявлений Collides.


Вы также можете просто вызовите другую делегирующую функцию:

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

, которая будет учитывать будущие Collides функции из-за ADL.

4 голосов
/ 28 апреля 2020

Нет способа удалить шаблоны функций из набора перегрузки. В вашем конкретном случае есть обходные пути, такие как:

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);
    }
}
...