反转reverse_iterator是否应该给出原始类型的前向迭代器?

问题描述 投票:0回答:2

我天真地期望这个程序能够编译并以成功状态运行:

#include <iterator>
#include <string>

int main()
{
    const std::string s = "foo";
    auto forward_iter = s.begin();
    auto reverse_iter = std::make_reverse_iterator(forward_iter);
    auto third_iter = std::make_reverse_iterator(reverse_iter);
    return forward_iter != third_iter;
}

编译失败,因为

third_iter
的类型和我们一开始的
forward_iter
的类型不一样;相反,它是一个
reverse_iterator<reverse_iterator<normal_iterator>>

0.cpp:10:25: error: no match for ‘operator!=’ (operand types are ‘__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >’ and ‘std::reverse_iterator<std::reverse_iterator<__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> > > >’)
     return forward_iter != third_iter;
            ~~~~~~~~~~~~~^~~~~~~~~~~~~

重新阅读文档,似乎

std::make_reverse_iterator(it)
被指定为始终包装
it
即使
it
已经是一个反向迭代器
(在某种程度上这是有道理的,因为我们期望使用
reverse_iterator
成员(即
base()
)。

是否有一种标准方法可以在普通(正向)迭代器和包装(反向)迭代器之间进行交换,而不知道我拥有哪种类型? 或者我是否需要编写一对 SFINAE 函数来适当地返回

std::make_reverse_iterator(it)
it.base()

c++ iterator reverse-iterator
2个回答
2
投票

反转reverse_iterator是否应该给出原始类型的前向迭代器?

不。或者至少,这不是

make_reverse_iterator
的指定方式。标准中指定返回类型为
reverse_iterator<Iterator>

是否有一种标准方法可以在普通(正向)迭代器和包装(反向)迭代器之间进行交换,而不知道我拥有哪种类型?

不。据我所知还没有。

或者我是否需要编写一对 SFINAE 函数来适当地返回

std::make_reverse_iterator(it)
it.base()

你可以写。我不能说你是否需要它。

这是一种实现:

#include <iterator>
#include <type_traits>

// https://stackoverflow.com/a/35408829/2079303
template<typename I>
struct is_reverse_iterator : std::false_type {};

template<typename I>
struct is_reverse_iterator<std::reverse_iterator<I>>
: std::integral_constant<bool, !is_reverse_iterator<I>::value> {};

template<class It>
auto
reverse_or_base(It&& i)
{
    if constexpr (is_reverse_iterator<std::decay_t<It>>())
        return i.base();
    else
        return std::make_reverse_iterator(std::forward<It>(i));
}

还有一个测试:

#include <vector>
#include <cassert>
int main() {
    std::vector<int> v;
    static_assert(
        std::is_same_v<
            decltype(reverse_or_base(v.rbegin())),
            std::vector<int>::iterator
        >
    );
    assert(v.end() == reverse_or_base(v.rbegin()));

    static_assert(
        std::is_same_v<
            decltype(reverse_or_base(v.begin())),
            std::vector<int>::reverse_iterator
        >
    );
    assert(v.rend() == reverse_or_base(v.begin()));
}

2
投票

我设法制作了一对小函数来在正向和反向迭代器之间交换(使用简单的重载而不是 SFINAE):

template<typename Iter>
auto toggle_iterator_direction(Iter it) {
    return std::make_reverse_iterator(it);
}

template<typename Iter>
auto toggle_iterator_direction(std::reverse_iterator<Iter> it) {
    return it.base();
}

还有一个替代版本,使用辅助类型模板,建模为 确定 (c++) 迭代器是否是反向的的答案:

template<typename Iter>
struct is_reverse_iterator : std::false_type {};

template<typename Iter>
struct is_reverse_iterator<std::reverse_iterator<Iter>> : std::true_type {};


template<typename Iter>
auto toggle_iterator_direction(Iter it) {
    if constexpr (is_reverse_iterator<Iter>())
        return it.base();
    else
        return std::make_reverse_iterator(it);
}

这两个都修复了修改后的测试程序:

#include <iterator>
#include <string>

int main()
{
    const std::string s = "foo";
    auto forward_iter = s.begin();
    auto reverse_iter = toggle_iterator_direction(forward_iter);
    auto third_iter = toggle_iterator_direction(reverse_iter);
    return forward_iter != third_iter;
}
© www.soinside.com 2019 - 2024. All rights reserved.