为 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
,避免了在非推导上下文中使用 P
(P
不在某些 ::
符号的左侧)。不幸的是,现在 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
里面定义也可以,看你的选择。)
我有以下 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
,避免了在非推导上下文中使用 P
(P
不在某些 ::
符号的左侧)。不幸的是,现在 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
里面定义也可以,看你的选择。)