我想实现一些通用算法,并且我有很多想法可以根据与算法一起使用的实体的某些特征来实现专用算法。但是,我似乎没有想出所有的特殊特征,我想实现通用版本,以便它们可以与另一个专用版本一起使用。
例如,考虑 distance(begin, end)
(是的,我知道它在标准库中;但是,它很好很简单,可以用来演示我的问题)。一般版本可能如下所示(我使用 std::ptrdiff_t
而不是 std::iterator_traits<It>::difference_type
作为另一个简化):
template <typename It>
auto distance(It it, It end) -> std::ptrdiff_t {
std::ptrdiff_t size{};
while (it != end) {
++it;
++size;
}
return size;
}
当然,如果迭代器类型是随机访问迭代器,那么利用两个迭代器之间的差异来实现算法要好得多。天真地添加
template <typename It>
auto distance(It begin, It end)
-> typename std::enable_if<is_random_access_v<It>, std::ptrdiff_t>::type {
return end - begin;
}
不太好用:两种实现都同样适合随机访问迭代器,即编译器认为它们是模棱两可的。处理这种情况的简单方法是将一般实现更改为仅适用于非随机访问迭代器。也就是说,SFINAE 的选择是相互排斥的,同时也覆盖了整个空间。
不幸的是,这组实现仍然是封闭的:在不更改至少一个实现的签名的情况下,我无法添加另一个实现,以防我对利用特殊属性的通用实现有另一个想法。例如,如果我想为 分段范围 添加特殊处理(想法:当底层序列按原样由段组成时,例如 std::deque<...>
或 std::istreambuf_iterator<cT>
的情况,单独处理段)有必要将一般实现更改为仅在序列不是随机访问且不是分段序列时才适用。当然,如果我控制可以完成的实现。用户将无法扩展通用实现集。
我知道对于特殊的迭代器类型可以重载这些函数。但是,这将要求每次添加具有特殊功能的迭代器时,都需要实现相应的功能。目标是能够添加通用实现,这些实现是改进的,以防与它们一起使用的实体公开额外的设施。它类似于不同的迭代器类别,尽管属性与迭代器类别正交。
因此,我的问题是:
- 能否实现通用算法,以便在不更改现有实现的情况下添加新的改进想法?如果可以,如何实现?
- 可选的跟进(我主要对上面的问题感兴趣,但这个问题也可能很有趣):如果不可能,是否会在概念中添加此能力?
最佳答案
一种方法是基于排名的重载机制。为每个重载分配一个等级,然后让重载解析完成其余的工作。
这些是辅助特征:
template <unsigned i> struct rank : rank<i - 1> {};
template <> struct rank<0> {};
using call_ranked = rank<256>;
这是一个示例用法:
template <typename It>
auto distance_ranked(rank<0>, It it, It end) -> std::size_t {
std::size_t size{};
while (it != end) {
++it;
++size;
}
return size;
}
template <typename It>
auto distance_ranked(rank<1>, It begin, It end)
-> typename std::enable_if<is_random_access_v<It>, std::size_t>::type {
return end - begin;
}
// Delegating function template:
template <typename... Args>
auto distance(Args&&... args)
-> decltype(distance_ranked(call_ranked(), std::forward<Args>(args)...)) {
return distance_ranked(call_ranked(), std::forward<Args>(args)...);
}
Demo .
具有较高编号的等级比具有较低编号的等级具有更高的优先级。 IE。 rank<1>
如果匹配项相同,则导致第二个重载被选择而不是第一个重载 ( rank<0>
)。
如果您想添加基于段的实现,请将其用作 enable_if
的条件.大概分段范围和随机访问范围是互斥的,但如果不是,则为随机访问分配更高的优先级。一般准则可能是:实现越高效,其排名就越高。
使用此方法,在引入新方法时不应影响其他实现。必须确保具有非空交叉点的任何两个类别(未被更高等级的类别涵盖)具有不同的等级 - 这构成了一个明显的缺点。
关于c++ - 泛型函数的重载可以对其他重载开放吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27508239/