在定义可交换操作时减少代码重复

mbo*_*zzi 50 c++ templates metaprogramming

我有一组名为的可交换二进制函数的重载overlap,它接受两种不同的类型:

class A a; class B b;
bool overlap(A, B);
bool overlap(B, A);
Run Code Online (Sandbox Code Playgroud)

overlap当且仅当一个形状与另一个形状重叠时,我的函数返回true - 这是讨论multimethods时使用的一个常见示例.

因为overlap(a, b)相当于overlap(b, a),我只需要实现关系的一个"侧".一个重复的解决方案是写这样的东西:

bool overlap(A a, B b) { /* check for overlap */ }
bool overlap(B b, A a) { return overlap(a, b);   }
Run Code Online (Sandbox Code Playgroud)

但我宁愿不N! / 2使用模板生成相同功能的额外简单版本.

template <typename T, typename U> 
bool overlap(T&& t, U&& u) 
{ return overlap(std::forward<U>(u), std::forward<T>(t)); }
Run Code Online (Sandbox Code Playgroud)

不幸的是,这很容易无限递归,这是不可接受的:见 http://coliru.stacked-crooked.com/a/20851835593bd557

如何防止这种无限递归?我正确地解决了这个问题吗?

Que*_*tin 64

这是一个简单的修复:

template <typename T, typename U> 
void overlap(T t, U u)
{
    void overlap(U, T);
    overlap(u, t);
}
Run Code Online (Sandbox Code Playgroud)

模板本身声明了目标函数,它比递归更受欢迎,因为它是完全匹配的(确保在实际情况下处理constness和reference-ness).如果该函数尚未实现,则会出现链接器错误:

/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
Run Code Online (Sandbox Code Playgroud)

...直接指向缺失的函数:)

  • 直到链接时间对于具有许多源文件的大型项目来说可能是一个很大的缺点,才会检测到错误.更改`.h`会导致很多文件需要重新编译,因此链接时间可能需要几分钟的编译时间.希望这很容易做到,并且不需要在大多数代码库中经常重新调整. (2认同)

Ded*_*tor 12

正如一位聪明人曾经说过的那样,除了太多的间接层之外,没有任何问题你无法用额外的间接层来解决.

因此,使用SFINAE和一些间接来完成它:

template<class A, class B>
auto overlap(A&& a, B&& b)
-> decltype(overlap_impl('\0', std::forward<A>(a), std::forward<B>(b)))
{ return overlap_impl('\0', 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.
Run Code Online (Sandbox Code Playgroud)