【问题标题】:Call a function that is specifically not templated调用专门未模板化的函数
【发布时间】:2020-04-28 17:01:55
【问题描述】:

我有一堆函数可以检查各种形状之间的碰撞。

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++ 标记或功能允许您关闭或禁止指定行或块的参数类型推导?目的是强制编译器查找非模板化实现,如果不存在则编译失败。

【问题讨论】:

  • @royseph 当存在同名且参数类型合适的非模板函数时,甚至不选择模板函数。

标签: c++ templates template-meta-programming


【解决方案1】:

在函数声明期间(在打开{ 之前)不查找函数模板的时间。利用这一点,我们可以将 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

由于 ADL,这将考虑到未来的 Collides 功能。

【讨论】:

  • 所以让我直截了当,因为在我们到达 return 语句时,函数实际上还没有被声明,所以它不能被递归调用。 ?
  • @Troyseph 在正文中,模板是可以调用的可行函数的一部分,但非模板函数总是更好的匹配,因此会被调用。但是,如果没有其他匹配项,我们将永远无法到达身体。
  • @Artyer 非常简洁,那么返回类型到底是什么?它看起来像一个函数?还是我误解了decltype的用法
  • 返回类型是“whatever Collides(b, a)返回”。
  • 啊我明白了!如果没有函数Collides(b, a),则编译器无法推断模板的返回类型,编译失败,编译器无法使用auto返回类型来计算类型,因此无法使用自己定义其返回输入任何一个。
【解决方案2】:

无法从重载集中删除函数模板。在您的特定情况下,有一些解决方法,例如:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-05
    • 1970-01-01
    • 2016-07-02
    • 2014-10-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多