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

时间:2016-05-18 22:51:34

标签: c++ templates inner-classes argument-dependent-lookup name-lookup

我有以下课程:

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

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

技术1(不起作用):使用依赖于参数的查找(ADL)。在A的正文中定义以下内部,但在B的正文之外:

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

由于ADL只在名称空间内而不是类范围内查找,所以它不会起作用。 更新: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未知),以及其他类模板(用于多项式环和字段扩展)。问题是如果P在非推断的上下文中,我不确定如何使用SFINAE。

2 个答案:

答案 0 :(得分:3)

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

然后让ADL发挥其魔力。

(在B内定义也没关系。选择取决于你。)

答案 1 :(得分:0)

这将有效:

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位于同一名称空间中。此外,我们通过将模板参数再次传递给P,避免在未推断的上下文中P::不在某个B符号的左侧)。很遗憾,现在B不是A的成员,因此如果您想让它访问A的成员,您将不得不将其变成朋友。此前的解决方案归因于this answer。我们接下来会改进它。

或者,如果您真的希望B成为A的成员,那么您只需要公共外观以确保正确的重定向:

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

这里的优势在于,这个公共门面可以重复使用几个课程,并且它不一定是朋友。