更改类型后递归为模板化函数

Recurse into a templated function after changing the type

本文关键字:函数 递归 类型      更新时间:2023-10-16

我目前正在编写自己的合并排序实现以进行实践。合并列表的左侧和右侧部分时,创建仅包含两侧较小部分的临时列表是有意义的。但是,逻辑会根据复制到临时的一侧而变化。

我的函数具有以下签名:

template<class RandomIt, class Compare>
void Merge(RandomIt begin, RandomIt middle, RandomIt end, Compare Comp);

我有一个聪明的想法,在开始时检查[begin,middle)[middle,end)的长度,如果左边更大,则将迭代器转换为反向迭代器并递归调用该函数。喜欢这个:

template<class RandomIt, class Compare>
void Merge(RandomIt begin, RandomIt middle, RandomIt end, Compare Comp) {
    size_t leftLength = std::distance(begin, middle);
    size_t rightLength = std::distance(middle, end);
    if (leftLength > rightLength) {
        using ReverseIterator = std::reverse_iterator<RandomIt>;
        Merge(ReverseIterator(end), ReverseIterator(std::next(middle)), ReverseIterator(begin), Comp);
        return;
    }
    //Now [begin,middle) is guaranteed <= [middle,end)
    //...
}

但是,编译器给出了一个相当严重的错误。

fatal error: template instantiation depth exceeds maximum of 900 (use -ftemplate-depth= to increase the maximum)

我在 ideone.com 上创建了一个重现错误的小型独立应用程序。

编辑:对于合并排序,我刚刚意识到这不起作用,因为比较函数需要反转。

这里的问题是,您在递归的每一步都创建了函数Merge的新实例化,即第一次从randomaccess_iterator创建reverse_iterator时。然后编译器从reverse_iterator创建一个reverse_iterator,依此类推.....

正如您在创建reverse_iterator时必须注意到的那样,迭代器的模板类型不断变化。从而创建模板化函数的新实例。

简而言之,您对迭代器的使用是错误的。

这可能有效:

template <class RandomIt, class RevIt>
void Test(RandomIt begin, RandomIt middle, RandomIt end, RevIt rit) {
        size_t leftLength = std::distance(begin, middle);
        size_t rightLength = std::distance(middle, end);
        if (leftLength > rightLength) {
                Test(RevIt(end), RevIt(middle), RevIt(begin), rit);
                return;
        }
}
template <typename RandomIt>
void Test(RandomIt begin, RandomIt middle, RandomIt end) {
  Test(begin, middle, end, std::reverse_iterator<RandomIt>());
}
int main() {
        std::vector<int> nums = { 2, 1, 123, 1, 23, 123, 123, 5234, 52, 3, 452, 3, 452, 5 };
        int middle;
        std::cin >> middle;
        Test(nums.begin(), nums.begin()+middle, nums.end());
        return 0;
}

问题是Merge<it>需要Merge<std::reverse_iterator<it>>,这需要Merge<std::reverse_iterator<std::reverse_iterator<it>>,这需要Merge<std::reverse_iterator<std::reverse_iterator<std::reverse_iterator<it>>>需要Merge<std::reverse_iterator<std::reverse_iterator<std::reverse_iterator<std::re‌​verse_iterator<it>>>>,这需要......

因此,您想要的是已经反向迭代器的ReverseIterator(使用原始迭代器。因此,您需要帮助程序函数:

template <class RandomIt>
RandomIt ReverseIt(std::reverse_iterator<RandomIt> it) {
    return it.base();
}
template <class RandomIt>
std::reverse_iterator<RandomIt> ReverseIt(RandomIt it) {
    return std::reverse_iterator<RandomIt>(it);
}
template<class RandomIt, class Compare=std::less<typename std::iterator_traits<RandomIt>::value_type>>
void Merge(RandomIt begin, RandomIt middle, RandomIt end, Compare Comp={}) {
    size_t leftLength = std::distance(begin, middle);
    size_t rightLength = std::distance(middle, end);
    if (leftLength > rightLength) {
        Merge(ReverseIt(end), ReverseIt(std::next(middle)), ReverseIt(begin), Comp);
        return;
    }
    //Now [begin,middle) is guaranteed <= [middle,end)
    //...
}

编译证明:http://coliru.stacked-crooked.com/a/741d73f889594e36