【问题标题】:std::find() backwards on C-style array?std::find() 在 C 样式数组上倒退?
【发布时间】:2012-04-08 15:48:16
【问题描述】:

说我需要使用s

typedef struct tagSOMESTRUCT   // Defined by someone else; C-compatible
{
    int count;
    int elements[256];
} SOMESTRUCT;

SOMESTRUCT s;

说我有这样的功能:

template<typename RevFwdIt>
std::pair<RevFwdIt, RevFwdIt> some_slice_rev(RevFwdIt rbegin, RevFwdIt rend)
{
    RevFwdIt it = basename_rev(rbegin, rend);
    RevFwdIt e = std::find(rbegin, it, 5);
    return std::make_pair(e == it ? rbegin : e, it);
}

为了使用这个功能,我需要说

some_slice_rev(&s.elements[s.count - 1], &s.elements[-1]);

哪一个(恕我直言)是丑陋且容易出错的,因为存在非一个错误。

一方面,我不能简单地将some_slice_rev 更改为some_slice 以使用(更好)

some_slice(&s.elements[0], &s.elements[s.count]);

因为std::find 会从头而不是尾搜索。

另一方面,代码本身在我看来已经损坏,因为我看不出std::find 将如何处理作为原始指针的“反向迭代器”。

在这种情况下修复代码的最佳方法是什么?有没有办法使用作为原始指针的反向迭代器?或者,除了更改SOMESTRUCT其他是否有解决此问题的标准重构机制?

【问题讨论】:

    标签: c++ pointers iterator find


    【解决方案1】:

    我不太确定我是否理解这个问题(这可能是由于您似乎试图避免的迭代器方向的尴尬混合),但我只是将您的注意力引向std::reverse_iterator

    #include <iostream>
    #include <iterator>
    
    // for example
    template <typename Iter>
    void print_it(Iter first, Iter last)
    {
        std::cout << '|';
    
        for (; first != last; ++first)
            std::cout << ' ' << *first << " |";
    
        std::cout << std::endl;
    }
    
    int main()
    {
        int arr[10] = {1, 2, 3, 4};
    
        int *begin = arr, *end = arr + 4;
    
        print_it(begin, end);
        print_it(std::reverse_iterator<int*>(end),
                    std::reverse_iterator<int*>(begin));
    }
    

    它们像双向迭代器一样工作,除了 ++ 在内部是 --,反之亦然。

    请注意,它有点难看。您可能需要一些实用功能:

    #include <iostream>
    #include <iterator>
    
    // for example
    template <typename Iter>
    void print_it(Iter first, Iter last)
    {
        std::cout << '|';
    
        for (; first != last; ++first)
            std::cout << ' ' << *first << " |";
    
        std::cout << std::endl;
    }
    
    template <typename Iter>
    std::reverse_iterator<Iter> make_reverse_iterator(Iter iter)
    {
        return std::reverse_iterator<Iter>(iter);
    }
    
    int main()
    {
        int arr[10] = {1, 2, 3, 4};
    
        int *begin = arr, *end = arr + 4;
    
        print_it(begin, end);
        print_it(make_reverse_iterator(end),
                    make_reverse_iterator(begin));
    }
    

    所以我想你想要这个:

    template<typename ForwardIterator >
    std::pair<ForwardIterator, ForwardIterator>
        some_slice(ForwardIterator begin, ForwardIterator end)
    {
        typedef std::reverse_iterator<ForwardIterator> rev_iter;
    
        rev_iter it = basename(rev_iter(end), rev_iter(begin));
        rev_iter e = std::find(rev_iter(end), it, 5);
    
        return std::make_pair(it.base(), e.base());
    }
    

    现在相对偏离主题,但请注意,如果 s.count256s.elements[s.count] 是未定义的行为,因为 s.elements[s.count]*(s.elements + s.count),这不是要取消引用的有效数组元素。

    实际上,完整的表达式很好,因为&amp;*x 抵消了x,但您仍然可能想避免它:

    some_slice(s.elements, s.elements + s.count);
    

    s.elements[-1] 也可能是未定义的行为,但我认为严格来说它可能是偶然的,因为您在数组之前有一个 int 成员。

    【讨论】:

    • 哦...哇。我不知道像这样实例化reverse_iterator 是合法的......认为它必须在内部实例化。 +1 这很有帮助。但是,后续行动:您是否知道任何通用的方法来创建 some_slice 围绕 some_slice_rev 的包装器? (我不能简单地说some_slice(make_reversed(end - 1), make_reversed(begin - 1)),因为这会破坏非原始指针类型。)
    • @Mehrdad:恐怕我又回到了不理解的状态。 :) 也许知道std::reverse_iterator 有一个base() 函数,以获取底层迭代器,有助于回答您的问题。
    • 对不起,我会尽量说得更清楚:问题是我希望能够在 C 样式数组上使用 find,但从数组的末尾开始。当然,这适用于任何具有反向迭代器(rbegin()rend())的容器,但它不适用于使用指针的 C 样式数组。我希望想要简单地创建一个使用前向迭代器的函数XYZ 来执行此操作,而不是创建一个尴尬的XYZ_rev 函数,但我不知道有什么好方法。有什么建议吗?
    • @Mehrdad:如果在我的示例中将print_it 替换为std::find,那就可以了。所以你想要一个函数,它最初采用前向迭代器,但在内部将它们反转一步?
    • 是的,完全正确。 :) 我该怎么做呢?我是否必须完全避免使用find,并创建自己的find_rev 函数? :\
    【解决方案2】:

    一个简单的解决方案是编写一个包装迭代器类来模拟反向迭代器,然后使用它来代替原始数组迭代器,像往常一样使用std::find。所以当std::find 调用++it 时,它会调用operator++,这会在内部递减实际的迭代器--rawIt。这就是其他标准反向迭代器所做的。

    【讨论】:

    • 我在想……可以这么说,这是“规范”的解决方案吗?还是没有众所周知的解决方案?
    • @Mehrdad:我认为,这就是其他 std 反向迭代器所做的。
    • 嗯...你知道任何通用的方法来创建 some_slice 包装器 some_slice_rev 代替吗? (我不能简单地说some_slice(make_reversed(end - 1), make_reversed(begin - 1)),因为这会破坏非原始指针类型。)
    • @Mehrdad:我认为,专业化将有助于做到这一点。只需使用少量元编程以不同方式处理每种情况。
    • 是的,专门针对指针当然不是问题。但是我如何让我的函数与其他不是指针的迭代器兼容呢? (begin - 1 不一定对它们有效。)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-20
    • 2011-01-26
    • 1970-01-01
    • 2017-07-18
    • 1970-01-01
    相关资源
    最近更新 更多