为 class 模板的特定嵌套 class 实现非成员泛型函数

Implementing non-member generic function for specific nested class of a class template

我有以下 class:

template<int P>
struct A {
    struct B {
        auto abs() const { return 1; }
    };
};

具体来说,A 应该是整数模 P 的有限域,而 B 是代表这个有限域中元素的 class。然后我有一个扩展的 GCD 算法的通用实现,它应该使用 abs 函数。我希望扩展的 GCD 算法实现能够处理基本类型(int、long 等)以及 A<P>::B 的问题。所以我需要实现一个将调用 A<P>::B::abs 的非成员 abs。我尝试了三种技术,其中两种不起作用,一种起作用但不适合。

技巧 1(不起作用):使用参数相关查找 (ADL)。定义以下 A 的正文中 但在 B 的正文之外:

auto abs(B const &e) { return e.abs(); }

它不会工作,因为 ADL 只在命名空间内部查找,而不是 class 范围。 UPDATE :ADL 在这里不起作用,因为 abs 是一个实例方法。然而,根据 T.C. 的回答,如果您将它设为友元函数,它会正常工作并且 ADL 会找到它!

技巧 2(不起作用):在全局或命名空间范围内使用约束函数模板:

template<int P>
auto abs(typename A<P>::B const &e) { return e.abs(); }

这也不起作用,因为 P 处于非推导上下文中。

技巧 3(有效,但不令人满意):在全局或命名空间范围内使用不受约束的函数模板:

template<typename T>
auto abs(T const &e) { return e.abs(); }

这行得通。然而,它是不受约束的。我想将此函数模板的实例化限制为 A<P>(对于事先未知的 P)和其他 class 模板(对于多项式环和字段扩展)。问题是如果 P 处于非推导上下文中,我不确定如何使用 SFINAE。

这将起作用:

namespace impl {
    template <int P>
    struct B {
        auto abs() const { return 1; }
    };

    template <int P>
    auto abs(B<P>& b) {
        return b.abs();
    }
}

template <int P>
struct A {
    using B = impl::B<P>;
    friend B;
};

int main() {
    A<3>::B inner;
    abs(inner);
}

此解决方案使用 ADL,因为 abs 现在与 B 位于同一名称空间中。此外,我们通过再次将模板参数传递给 B,避免了在非推导上下文中使用 PP 不在某些 :: 符号的左侧)。不幸的是,现在 B 不是 A 的成员,所以如果你想让它访问 A 的成员,你必须将它加为好友。以前的解决方案是由于 this answer。接下来我们对其进行改进。

或者,如果您真的希望 B 成为 A 的成员,那么您只需要一个 public 门面来确保正确的重定向:

namespace impl {
    template <typename T>
    struct facade : public T {};

    template <typename T>
    auto abs(facade<T>& b) { return b.abs();    }
}

template <int P>
struct A {
    struct B_ {
        auto abs() const { return 1; }
    };

    using B = impl::facade<B_ >;
};

int main() {
    A<3>::B inner;
    abs(inner);
}

这里的优点是这个 public 门面可重复用于多个 类,而且它不必是朋友。

template<int P>
struct A {
    struct B {
        auto abs() const { return 1; }
    };
    friend auto abs(B const &e) { return e.abs(); }
//  ^^^^^^
};

那就让 ADL 发挥它的魔力吧。

(在B里面定义也可以,看你的选择。)