提高迭代器转换const_iterator

Boost const_iterator to iterator conversion

本文关键字:iterator const 转换 迭代器      更新时间:2023-10-16

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

方法 1 定义一个iterator<T>类。iterator<const T>将代表一个const_iterator.iterator<T>有一个返回iterator<const T>的转换运算符。对于模板函数,此操作失败,因为在模板实例化期间不会发生类型转换。

方法 2 在理论上有效。在实践中,我需要为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的好处。

有没有更好的方法来制作iteratorconst_iteratorboost/iterator

这是方法 1:

#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);

我会专门化模板:

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);
}

科里鲁现场快速演示

std::vector<int> v{ 0, 1, 2, 3 };
foo(v);
auto const &constv = v;
foo(constv);

指纹

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

强制常量迭代器

这似乎是您的代码的内容。所以,让我们强迫它!这是将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);
}

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

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模式。

完整列表

最后一个完整演示:

住在科里鲁

#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