在定义交换操作时减少代码重复
Reducing code duplication while defining a commutative operation
我有一组名为 overlap
的可交换二元函数重载,它接受两种不同的类型:
class A a; class B b;
bool overlap(A, B);
bool overlap(B, A);
我的函数 overlap
returns 当且仅当一个形状与另一个形状重叠时为真 - 这是讨论 multimethods.
时使用的一个常见示例
因为overlap(a, b)
等价于overlap(b, a)
,我只需要实现一个"side"关系。一种重复的解决方案是写这样的东西:
bool overlap(A a, B b) { /* check for overlap */ }
bool overlap(B b, A a) { return overlap(a, b); }
但我不想编写额外的 N! / 2
同一函数的简单版本,而是允许使用模板生成它们。
template <typename T, typename U>
bool overlap(T&& t, U&& u)
{ return overlap(std::forward<U>(u), std::forward<T>(t)); }
不幸的是,这很容易无限递归,这是不可接受的:见
http://coliru.stacked-crooked.com/a/20851835593bd557
如何防止这种无限递归?我是否正确地处理了问题?
这是一个简单的修复方法:
template <typename T, typename U>
void overlap(T t, U u)
{
void overlap(U, T);
overlap(u, t);
}
模板本身声明了目标函数,它比递归更受青睐,因为它是完全匹配的(一定要注意实际情况中的常量性和引用性)。如果该功能尚未实现,您将收到链接器错误:
/tmp/cc7zinK8.o: In function `void overlap<C, D>(C, D)':
main.cpp:(.text._Z7overlapI1C1DEvT_T0_[_Z7overlapI1C1DEvT_T0_]+0x20):
undefined reference to `overlap(D, C)'
collect2: error: ld returned 1 exit status
... 直接指向缺少的函数:)
您可以将实际方法重命名为 overlap_impl
之类的名称,然后在模板中调用该方法。我将打破递归:
bool overlap_impl(A a, B b) { /* check for overlap */ }
template <typename T, typename U>
bool overlap(T&& t, U&& u)
{ return overlap_impl(std::forward<U>(u), std::forward<T>(t)); }
template<> bool overlap(A&& t, B&& u)
{ return overlap_impl(std::forward<A>(t), std::forward<B>(u)); }
一位智者曾经说过,没有任何问题是通过额外的间接层解决不了的,除非间接层太多。
因此,使用 SFINAE 和一些间接方法来完成它:
template<class A, class B>
auto overlap(A&& a, B&& b)
-> decltype(overlap_impl('[=10=]', std::forward<A>(a), std::forward<B>(b)))
{ return overlap_impl('[=10=]', std::forward<A>(a), std::forward<B>(b)); }
template<class A, class B>
auto overlap_impl(int, A&& a, B&& b)
-> decltype(do_overlap(std::forward<A>(a), std::forward<B>(b)))
{ return do_overlap(std::forward<A>(a), std::forward<B>(b)); }
template<class A, class B>
auto overlap_impl(long, B&& b, A&& a)
-> decltype(do_overlap(std::forward<A>(a), std::forward<B>(b)))
{ return do_overlap(std::forward<A>(a), std::forward<B>(b)); }
// You can provide more choices if you want, for example to use member-functions.
// Implement `do_overlap(A, B)`, maybe with references, in at least one direction.
我有一组名为 overlap
的可交换二元函数重载,它接受两种不同的类型:
class A a; class B b;
bool overlap(A, B);
bool overlap(B, A);
我的函数 overlap
returns 当且仅当一个形状与另一个形状重叠时为真 - 这是讨论 multimethods.
因为overlap(a, b)
等价于overlap(b, a)
,我只需要实现一个"side"关系。一种重复的解决方案是写这样的东西:
bool overlap(A a, B b) { /* check for overlap */ }
bool overlap(B b, A a) { return overlap(a, b); }
但我不想编写额外的 N! / 2
同一函数的简单版本,而是允许使用模板生成它们。
template <typename T, typename U>
bool overlap(T&& t, U&& u)
{ return overlap(std::forward<U>(u), std::forward<T>(t)); }
不幸的是,这很容易无限递归,这是不可接受的:见 http://coliru.stacked-crooked.com/a/20851835593bd557
如何防止这种无限递归?我是否正确地处理了问题?
这是一个简单的修复方法:
template <typename T, typename U>
void overlap(T t, U u)
{
void overlap(U, T);
overlap(u, t);
}
模板本身声明了目标函数,它比递归更受青睐,因为它是完全匹配的(一定要注意实际情况中的常量性和引用性)。如果该功能尚未实现,您将收到链接器错误:
/tmp/cc7zinK8.o: In function `void overlap<C, D>(C, D)':
main.cpp:(.text._Z7overlapI1C1DEvT_T0_[_Z7overlapI1C1DEvT_T0_]+0x20):
undefined reference to `overlap(D, C)'
collect2: error: ld returned 1 exit status
... 直接指向缺少的函数:)
您可以将实际方法重命名为 overlap_impl
之类的名称,然后在模板中调用该方法。我将打破递归:
bool overlap_impl(A a, B b) { /* check for overlap */ }
template <typename T, typename U>
bool overlap(T&& t, U&& u)
{ return overlap_impl(std::forward<U>(u), std::forward<T>(t)); }
template<> bool overlap(A&& t, B&& u)
{ return overlap_impl(std::forward<A>(t), std::forward<B>(u)); }
一位智者曾经说过,没有任何问题是通过额外的间接层解决不了的,除非间接层太多。
因此,使用 SFINAE 和一些间接方法来完成它:
template<class A, class B>
auto overlap(A&& a, B&& b)
-> decltype(overlap_impl('[=10=]', std::forward<A>(a), std::forward<B>(b)))
{ return overlap_impl('[=10=]', std::forward<A>(a), std::forward<B>(b)); }
template<class A, class B>
auto overlap_impl(int, A&& a, B&& b)
-> decltype(do_overlap(std::forward<A>(a), std::forward<B>(b)))
{ return do_overlap(std::forward<A>(a), std::forward<B>(b)); }
template<class A, class B>
auto overlap_impl(long, B&& b, A&& a)
-> decltype(do_overlap(std::forward<A>(a), std::forward<B>(b)))
{ return do_overlap(std::forward<A>(a), std::forward<B>(b)); }
// You can provide more choices if you want, for example to use member-functions.
// Implement `do_overlap(A, B)`, maybe with references, in at least one direction.