将Boost const_iterator转换为iterator

3

我尝试了两种方法来实现从const_iterator到iterator的转换。所有迭代器都基于boost/iterator

第一种方法定义了一个iterator<T>类。 iterator<const T>将代表const_iteratoriterator<T>具有一个转换运算符,返回一个iterator<const T>。但这在模板函数中会失败,因为在模板实例化期间不能进行任何类型转换。

第二种方法理论上是可行的。但在实践中,我需要为iterator<T>定义每个方法:

#include <iostream>
#include <boost/iterator/iterator_adaptor.hpp>
#include <vector>

template<typename Container>
class Cit
        : public boost::iterator_adaptor<
                Cit<Container>, // Derived
                typename Container::const_iterator, // Base
                const typename Container::value_type> {
    using self_type = Cit<Container>;
    friend class boost::iterator_core_access;
public:
    explicit Cit(typename Container::const_iterator it)
            : self_type::iterator_adaptor_(it) {}
};


template<typename Container>
class It : public Cit<Container> {
protected:
    using reference = typename Container::reference;
    using self_type = It<Container>;
    using Base = Cit<Container>;
public:
    explicit It(typename Container::iterator it)
            : Base(it) {}

    reference operator*() const {
        return const_cast<reference>(Base::operator*());
    }
    // Try to hide every method from Cit<Container>
    // ... 
    // ... 
    // ... 
    // ... oh well.
private:
    friend class boost::iterator_core_access;
};

// A template function
template<typename Container>
void foo(Cit<Container> it_begin,
         Cit<Container> it_end) {
    for (auto it = it_begin; it != it_end; ++it) {
        std::cout << *it << "\n";
    }

}

int main() {
    typedef std::vector<int> Container;
    Container v = {0, 1, 2, 3};  // content array
    It<Container> it_begin(v.begin());
    It<Container> it_end(v.end());
    // Assert It can implicitly convert to Cit even during template 
    // instantiation.
    foo(it_begin, it_end);
    return 0;
}

这似乎抵消了使用boost/iterator的好处。

是否有更好的方法来使用boost/iterator创建iterator和const_iterator?

以下是第一种方法:

#include <iostream>
#include <boost/iterator/iterator_adaptor.hpp>
#include <vector>

template<typename Container>
class It
        : public boost::iterator_adaptor<
                It<Container>, // Derived
                typename Container::const_iterator, // Base
                typename std::conditional<std::is_const<Container>::value,
                        const typename Container::value_type,
                        typename Container::value_type
                        >::type // Value
        > {
    using self_type = It<Container>;
    friend class boost::iterator_core_access;
public:
    explicit It(typename Container::const_iterator it)
            : self_type::iterator_adaptor_(it) {}
};

template <typename C> using Cit = It<const C>;

// A template function
template<typename Container>
void foo(Cit<Container> it_begin,
         Cit<Container> it_end) {
    for (auto it = it_begin; it != it_end; ++it) {
        std::cout << *it << "\n";
    }

}

int main() {
    typedef std::vector<int> Container;
    Container v = {0, 1, 2, 3};  // content array
    It<Container> it_begin(v.begin());
    It<Container> it_end(v.end());
    // Assert It can implicitly convert to from Cit to It even
    // during template instantiation.
    foo(it_begin, it_end);
    return 0;
}

错误信息:

error: no matching function for call to ‘foo(It<std::vector<int> >&, It<std::vector<int> >&)’
     foo(it_begin, it_end);
                         ^
main.cpp:26:6: note: candidate: template<class Container> void foo(Cit<Container>, Cit<Container>)
 void foo(Cit<Container> it_begin,
      ^~~
main.cpp:26:6: note:   template argument deduction/substitution failed:
main.cpp:41:25: note:   types ‘const C’ and ‘std::vector<int>’ have incompatible cv-qualifiers
     foo(it_begin, it_end);

你能展示一下方法1发生了什么吗?我认为那应该是它应该工作的方式。 - JDługosz
对于模板函数来说,这种方法不起作用,因为如果T不是const类型,iterator<T>和iterator<const T>是两种不同的类型。而且对于模板函数来说,无法在模板实例化期间进行类型转换,因此无法在两者之间进行转换。 - R zu
你可以尝试在Boost邮件列表上提问:请访问https://www.boost.org/community/groups.html - JDługosz
1个回答

1

我会专门化模板:

template <typename T>
class MyIt : public boost::iterator_adaptor<MyIt<T>,              // Derived
                                            typename T::iterator, // Base
                                            typename T::reference> {
    friend class boost::iterator_core_access;

  public:
    static constexpr bool is_const = false;
    explicit MyIt(typename MyIt::base_type it) : MyIt::iterator_adaptor_(it) {}
};

template <typename T>
class MyIt<T const> : public boost::iterator_adaptor<MyIt<T const>,              // Derived
                                                     typename T::const_iterator, // Base
                                                     typename T::const_reference> {
    friend class boost::iterator_core_access;

  public:
    static constexpr bool is_const = true;

    explicit MyIt(typename MyIt::base_type it) : MyIt::iterator_adaptor_(it) {}
};

虽然在这里已经允许转换,但如果您想要显式地进行“to-const-cast”,那么很容易编写:

template <typename T>
static MyIt<T const> make_const(MyIt<T> it) { return MyIt<T const>(it.base()); }

使用它:
// A template function
template <typename It> void foo(It it_begin, It it_end) {
    static_assert(It::is_const == std::is_const<typename std::remove_reference<decltype(*it_begin)>::type>::value, "mismatch");
    if (It::is_const)
        std::cout << "Const: ";

    for (auto it = it_begin; it != it_end; ++it)
        std::cout << *it << " ";

    std::cout << "\n";
}

正如您所看到的,我们的函数不关心特定的迭代器(这就是迭代器的全部意义)。您可以将其用于const和non-const:

template <typename C> void foo(C const &c) {
    MyIt<C const> b(c.begin()), e(c.end());
    foo(b, e);
}

template <typename C> void foo(C &c) {
    MyIt<C> b(c.begin()), e(c.end());
    foo(b, e);
}

Quick Demo Live On Coliru

 std::vector<int> v{ 0, 1, 2, 3 };
 foo(v);

 auto const &constv = v;
 foo(constv);

Prints

void foo(C&) [with C = std::vector<int>]
0 1 2 3 
void foo(const C&) [with C = std::vector<int>]
Const: 0 1 2 3

强制使用const迭代器

这似乎是你的代码关注的内容。因此,让我们来强制使用它!只需将MyIt<C>更改为MyIt<C const>

template <typename C> void foo(C &c) {
    MyIt<C const> b(c.begin()), e(c.end()); //  <--- note the const
    foo(b, e);
}

现在即使对于非 const 的 C,foo 也将使用 const 迭代器进行调用。如果您认为这很微妙,您可以使用上面显示的帮助程序:

template <typename C> void foo(C &c) {
    MyIt<C> b(c.begin()), e(c.end()); 
    foo(make_const(b), make_const(e)); //  <--- now more explicit?
}

当然,在foo中,您可以自由修改static_assert,以便它在第一次拒绝非const迭代器编译:

// A template function
template <typename It> void foo(It it_begin, It it_end) {
    static_assert(std::is_const<typename std::remove_reference<decltype(*it_begin)>::type>::value, "non-const disallowed");
    if (It::is_const)
        std::cout << "Const: ";

    for (auto it = it_begin; it != it_end; ++it)
        std::cout << *it << " ";

    std::cout << "\n";
}

你可以为任何 MyIt<> 添加重载以进行常量化:
template <typename C> 
typename std::enable_if<!std::is_const<C>::value>::type
foo(MyIt<C> b, MyIt<C> e) {
    foo(make_const(b), make_const(e));
}

因此,现在每次调用 foo 都会被强制进入 const 模式。

完整列表

最后一个演示的完整代码:

在 Coliru 上实时运行

#include <boost/iterator/iterator_adaptor.hpp>
#include <iostream>
#include <vector>

template <typename T>
class MyIt : public boost::iterator_adaptor<MyIt<T>,              // Derived
                                            typename T::iterator, // Base
                                            typename T::reference> {
    friend class boost::iterator_core_access;

  public:
    static constexpr bool is_const = false;
    explicit MyIt(typename MyIt::base_type it) : MyIt::iterator_adaptor_(it) {}
};

template <typename T>
class MyIt<T const> : public boost::iterator_adaptor<MyIt<T const>,              // Derived
                                                     typename T::const_iterator, // Base
                                                     typename T::const_reference> {
    friend class boost::iterator_core_access;

  public:
    static constexpr bool is_const = true;

    explicit MyIt(typename MyIt::base_type it) : MyIt::iterator_adaptor_(it) {}
};

template <typename T>
static MyIt<T const> make_const(MyIt<T> it) { return MyIt<T const>(it.base()); }

// A template function
template <typename It> void foo(It it_begin, It it_end) {
    static_assert(std::is_const<typename std::remove_reference<decltype(*it_begin)>::type>::value, "non-const disallowed");
    if (It::is_const)
        std::cout << "Const: ";

    for (auto it = it_begin; it != it_end; ++it)
        std::cout << *it << " ";

    std::cout << "\n";
}

template <typename C> 
typename std::enable_if<!std::is_const<C>::value>::type
foo(MyIt<C> b, MyIt<C> e) {
    foo(make_const(b), make_const(e));
}

template <typename C> void foo(C &c) {
    std::cout << __PRETTY_FUNCTION__ << "\n";
    MyIt<C> b(c.begin()), e(c.end());
    foo(b, e);
}

int main() {
    std::vector<int> v{ 0, 1, 2, 3 };
    foo(v);

    auto const &constv = v;
    foo(constv);
}

现在打印:

void foo(C&) [with C = std::vector<int>]
Const: 0 1 2 3 
void foo(C&) [with C = const std::vector<int>]
Const: 0 1 2 3 

谢谢。如果我只想让函数接受特定的迭代器,我应该使用特质来约束函数吗? - R zu
PS. 你可以使用一些decltypes或Boost Range traits将专业化合并到单个主模板中:http://coliru.stacked-crooked.com/a/d38e39f8c840746a(我还注意到我错误地使用了`reference`作为`value_type`,所以请将此注释视为错误修复。我稍后有时间会修改答案) - sehe
我认为真正的问题是“如何使用Boost.Iterator的适配器模板为新的集合类型提供常规迭代器和const迭代器的简便/惯用方式?” 我认为它不应该需要任何繁琐的步骤,而应该以某种内在的方式支持。 - JDługosz
我希望有一些简单的模板可以通过继承、const_cast或其他方法从const_iterator创建一个迭代器。而我所需要做的就是将迭代器类作为模板参数提供给这些模板。另一方面,也许作者以这种方式编写库是为了阻止用户编写只接受特定类型迭代器的函数。 - R zu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接