[英]const_iterator and constness of const_iterator::value_type
[英]How to remove constness of const_iterator?
作为这个问题的扩展Are const_iterators
faster? ,我还有一个关于const_iterators
的问题。 如何删除const_iterator
的常量性? 尽管迭代器是指针的通用形式,但const_iterator
和iterator
仍然是两个不同的东西。 因此,我相信,我也不能使用const_cast<>
从const_iterator
转换为iterator
s。
一种方法可能是您定义一个迭代器,它移动直到const_iterator
指向的元素。 但这看起来是一个线性时间算法。
关于什么是实现此目标的最佳方法的任何想法?
在 C++11 中有一个具有恒定时间复杂度的解决方案:对于任何序列、关联或无序关联容器(包括所有标准库容器),您可以使用空范围调用范围擦除成员函数:
template <typename Container, typename ConstIterator>
typename Container::iterator remove_constness(Container& c, ConstIterator it)
{
return c.erase(it, it);
}
range-erase 成员函数有一对const_iterator
参数,但它们返回一个iterator
。 因为提供了一个空范围,所以对erase 的调用不会改变容器的内容。
不幸的是,线性时间是唯一的方法:
iter i(d.begin());
advance (i,distance<ConstIter>(i,ci));
其中 iter 和 constIter 是合适的 typedef, d 是您正在迭代的容器。
在您上一篇文章的答案中,有几个人(包括我在内)出于与性能无关的原因推荐使用 const_iterators。 可读性、从设计板到代码的可追溯性……使用 const_iterators 提供对非常量元素的可变访问比根本不使用 const_iterators 要糟糕得多。 您正在将您的代码转换成只有您才能理解的东西,设计更糟糕,可维护性也很痛苦。 仅仅使用 const 来抛弃它比根本不使用 const 更糟糕。
如果你确定你想要它,C++ 的好/坏部分是你总能得到足够的绳子来吊死自己。 如果您打算使用 const_iterator 来解决性能问题,那么您真的应该重新考虑一下,但是如果您仍然想放手一搏……那么 C++ 可以提供您选择的武器。
首先,最简单的:如果您的操作将参数作为常量(即使在内部应用 const_cast),我相信它应该在大多数实现中直接工作(即使它可能是未定义的行为)。
如果你不能改变函子,那么你可以从任何一方解决这个问题:在 const 迭代器周围提供一个非常量迭代器包装器,或者在非常量函子周围提供一个 const 函子包装器。
迭代器外观,漫漫长路:
template <typename T>
struct remove_const
{
typedef T type;
};
template <typename T>
struct remove_const<const T>
{
typedef T type;
};
template <typename T>
class unconst_iterator_type
{
public:
typedef std::forward_iterator_tag iterator_category;
typedef typename remove_const<
typename std::iterator_traits<T>::value_type
>::type value_type;
typedef value_type* pointer;
typedef value_type& reference;
unconst_iterator_type( T it )
: it_( it ) {} // allow implicit conversions
unconst_iterator_type& operator++() {
++it_;
return *this;
}
value_type& operator*() {
return const_cast<value_type&>( *it_ );
}
pointer operator->() {
return const_cast<pointer>( &(*it_) );
}
friend bool operator==( unconst_iterator_type<T> const & lhs,
unconst_iterator_type<T> const & rhs )
{
return lhs.it_ == rhs.it_;
}
friend bool operator!=( unconst_iterator_type<T> const & lhs,
unconst_iterator_type<T> const & rhs )
{
return !( lhs == rhs );
}
private:
T it_; // internal (const) iterator
};
Scott Meyer关于首选迭代器而不是 const_iterators的文章回答了这个问题。 Visage 的答案是唯一安全的 pre-C++11 替代方案,但实际上是实现良好的随机访问迭代器的恒定时间,而其他的则是线性时间。
这可能不是您想要的答案,但有些相关。
我假设你想改变迭代器指向的东西。 我做的最简单的方法是 const_cast 返回的引用。
像这样的东西
const_cast<T&>(*it);
我相信在设计良好的程序中不需要这种转换。
如果您需要这样做 - 尝试重新设计代码。
作为解决方法,您可以使用以下方法:
typedef std::vector< size_t > container_type;
container_type v;
// filling container code
container_type::const_iterator ci = v.begin() + 3; // set some value
container_type::iterator i = v.begin();
std::advance( i, std::distance< container_type::const_iterator >( v.begin(), ci ) );
但我认为有时这种转换是不可能的,因为您的算法无法访问容器。
您可以从 const_iterator 中减去 begin() 迭代器以获得 const_iterator 指向的位置,然后将 begin() 添加回该位置以获得非常量迭代器。 我认为这对于非线性容器不是很有效,但是对于线性容器(例如向量),这将花费恒定的时间。
vector<int> v;
v.push_back(0);
v.push_back(1);
v.push_back(2);
v.push_back(3);
vector<int>::const_iterator ci = v.begin() + 2;
cout << *ci << endl;
vector<int>::iterator it = v.begin() + (ci - v.begin());
cout << *it << endl;
*it = 20;
cout << *ci << endl;
编辑:这似乎只适用于线性(随机访问)容器。
我认为想出一个解决方案来解决这个问题会很有趣,该解决方案适用于不在标准库中且不包含 erase() 方法的容器。
尝试使用它会导致 Visual Studio 2013 在编译时挂起。 我不包括测试用例,因为把它留给可以快速弄清楚界面的读者似乎是个好主意; 我不知道为什么这会挂在编译上。 即使 const_iterator 等于 begin(),也会发生这种情况。
// deconst.h
#ifndef _miscTools_deconst
#define _miscTools_deconst
#ifdef _WIN32
#include <Windows.h>
#endif
namespace miscTools
{
template < typename T >
struct deconst
{
static inline typename T::iterator iterator ( typename T::const_iterator*&& target, T*&& subject )
{
typename T::iterator && resultant = subject->begin ( );
bool goodItty = process < 0, T >::step ( std::move ( target ), std::move ( &resultant ), std::move ( subject ) );
#ifdef _WIN32
// This is just my habit with test code, and would normally be replaced by an assert
if ( goodItty == false )
{
OutputDebugString ( " ERROR: deconst::iterator call. Target iterator is not within the bounds of the subject container.\n" )
}
#endif
return std::move ( resultant );
}
private:
template < std::size_t i, typename T >
struct process
{
static inline bool step ( typename T::const_iterator*&& target, typename T::iterator*&& variant, T*&& subject )
{
if ( ( static_cast <typename T::const_iterator> ( subject->begin () + i ) ) == *target )
{
( *variant ) += i;
return true;
}
else
{
if ( ( *variant + i ) < subject->end () )
{
process < ( i + 1 ), T >::step ( std::move ( target ), std::move ( variant ), std::move ( subject ) );
}
else { return false; }
}
}
};
};
}
#endif
假设您的容器的const_iterator
具有与其iterator
相同的布局(对所有 STL 容器的有效假设),您可以简单地将前者位转换为后者:
#include <bit>
#include <vector>
void demo() {
using vec_t = std::vector<int>;
vec_t v { 1, 2, 3 };
vec_t::const_iterator c_it = v.cbegin();
vec_t::iterator it = std::bit_cast<vec_t::iterator>(c_it);
*it = 4; // OK, now v holds { 4, 2, 3 }
}
您可以将 const 迭代器值指针转换为非 const 值指针并直接使用它,如下所示
vector<int> v;
v.push_back(0);
v.push_back(1);
v.push_back(2);
v.push_back(2);
vector<int>::const_iterator ci = v.begin() + 2;
cout << *ci << endl;
*const_cast<int*>(&(*ci)) = 7;
cout << *ci << endl;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.