C++ 如何删除 const_iterator 的常量性?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/765148/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to remove constness of const_iterator?
提问by aJ.
As an extension to this question Are const_iterators
faster?, I have another question on const_iterators
. How to remove constness of a const_iterator
?
Though iterators are generalised form of pointers but still const_iterator
and iterator
s are two different things. Hence, I believe, I also cannot use const_cast<>
to covert from const_iterator
to iterator
s.
作为扩展到这个问题是const_iterators
更快?,我还有一个问题const_iterators
。如何删除 a 的常量const_iterator
?虽然迭代器是指针的一般形式,但仍然const_iterator
和iterator
s 是两个不同的东西。因此,我相信,我也不能用于const_cast<>
从const_iterator
to iterator
s 转换。
One approach could be that you define an iterator which moves 'til the element to which const_iterator
points. But this looks to be a linear time algorithm.
一种方法可能是您定义一个迭代器,它移动'直到const_iterator
指向的元素。但这看起来是一个线性时间算法。
Any idea on what is the best way to achieve this?
知道什么是实现这一目标的最佳方法吗?
回答by James McNellis
There is a solution with constant time complexity in C++11: for any sequence, associative, or unordered associative container (including all of the Standard Library containers), you can call the range-erase member function with an empty range:
在 C++11 中有一个具有恒定时间复杂度的解决方案:对于任何序列、关联或无序关联容器(包括所有标准库容器),您都可以使用空范围调用 range-erase 成员函数:
template <typename Container, typename ConstIterator>
typename Container::iterator remove_constness(Container& c, ConstIterator it)
{
return c.erase(it, it);
}
The range-erase member functions have a pair of const_iterator
parameters, but they return an iterator
. Because an empty range is provided, the call to erase does not change the contents of the container.
范围擦除成员函数有一对const_iterator
参数,但它们返回一个iterator
. 因为提供了一个空范围,所以对erase 的调用不会改变容器的内容。
回答by PaulJWilliams
Unfortunately linear time is the only way to do it:
不幸的是,线性时间是唯一的方法:
iter i(d.begin());
advance (i,distance<ConstIter>(i,ci));
where iter and constIter are suitable typedefs and d is the container over which you are iterating.
其中 iter 和 constIter 是合适的 typedef, d 是您正在迭代的容器。
回答by David Rodríguez - dribeas
In the answers to your previous post, there were a couple of people, me included, that recommended using const_iterators instead for non-performance related reasons. Readability, traceability from the design board to the code... Using const_iterators to provide mutating access to a non-const element is much worse than never using const_iterators at all. You are converting your code into something that only you will understand, with a worse design and a real maintainability pain. Using const just to cast it away is much worse than not using const at all.
在您上一篇文章的答案中,有几个人(包括我在内)出于与性能无关的原因推荐使用 const_iterators。可读性、从设计板到代码的可追溯性……使用 const_iterators 提供对非常量元素的可变访问比根本不使用 const_iterators 要糟糕得多。您正在将您的代码转换成只有您才能理解的东西,设计更糟糕,可维护性也很痛苦。仅仅使用 const 来抛弃它比根本不使用 const 更糟糕。
If you are sure you want it, the good/bad part of C++ is that you can always get enough rope to hang yourself. If your intention is using const_iterator for performance issues, you should really rethink it, but if you still want to shoot your foot off... well C++ can provide your weapon of choice.
如果你确定你想要它,C++ 的好/坏部分是你总能得到足够的绳子来吊死自己。如果您打算使用 const_iterator 来解决性能问题,那么您真的应该重新考虑一下,但是如果您仍然想放手一搏……那么 C++ 可以提供您选择的武器。
First, the simplest: if your operations take the arguments as const (even if internally apply const_cast) I believe it should work directly in most implementations (even if it is probably undefined behavior).
首先,最简单的:如果您的操作将参数作为常量(即使在内部应用 const_cast),我相信它应该在大多数实现中直接工作(即使它可能是未定义的行为)。
If you cannot change the functors, then you could tackle the problem from either side: provide a non-const iterator wrapper around the const iterators, or else provide a const functor wrapper around the non-const functors.
如果你不能改变函子,那么你可以从任何一方解决这个问题:在 const 迭代器周围提供一个非常量迭代器包装器,或者在非常量函子周围提供一个 const 函子包装器。
Iterator fa?ade, the long road:
迭代器外观,漫漫长路:
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
};
回答by Pontus Gagge
Scott Meyer's articleon preferring iterators over const_iterators answers this. Visage's answer is the only safe pre-C++11 alternative, but is actually constant time for well-implemented random access iterators, and linear time for others.
Scott Meyer关于首选迭代器而不是 const_iterators的文章回答了这个问题。Visage 的答案是唯一安全的 pre-C++11 替代方案,但实际上是实现良好的随机访问迭代器的恒定时间,而其他的则是线性时间。
回答by leiz
This may not be the answer you wanted, but somewhat related.
这可能不是您想要的答案,但有些相关。
I assume you want to change the thing where the iterator points to. The simplest way I do is that const_cast the returned reference instead.
我假设你想改变迭代器指向的东西。我做的最简单的方法是 const_cast 返回的引用。
Something like this
像这样的东西
const_cast<T&>(*it);
const_cast<T&>(*it);
回答by bayda
I believe this conversion is not needed in good designed program.
我相信在设计良好的程序中不需要这种转换。
If you need do this - try redesign code.
如果您需要这样做 - 尝试重新设计代码。
As workaround you can do next:
作为解决方法,您可以执行以下操作:
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 ) );
But I'm think that sometimes this conversion is impossible, because your algorithms can haven't access to container.
但我认为有时这种转换是不可能的,因为您的算法无法访问容器。
回答by moinudin
You can subtract the begin() iterator from the const_iterator to obtain the position the const_iterator is pointing to and then add begin() back to that to obtain a non-const iterator. I don't think this will be very efficient for non-linear containers, but for linear ones such as vector this will take constant time.
您可以从 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;
EDIT: This appears to only work for linear (random access) containers.
编辑:这似乎只适用于线性(随机访问)容器。
回答by Ankit
you can convert your const iterator value pointer to a non const value pointer and use it directly something like this
您可以将 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;
回答by user2813810
I thought it would be fun to come up with a solution to this that works for containers that aren't in the standard library and don't include the erase() method.
我认为想出一个解决方案来解决这个问题会很有趣,该解决方案适用于不在标准库中且不包含 erase() 方法的容器。
Attempting to use this causes Visual Studio 2013 to hang on compile. I'm not including the test case because leaving it to readers who can quickly figure out the interface seems like a good idea; I don't know why this hangs on compile. This occurs even when the const_iterator is equal to begin().
尝试使用它会导致 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