如何删除const_iterator的常数?

作为此问题的扩展,const_iterator更快吗?我在const_iterator上还有另一个问题。如何删除const_iterator的常数?尽管迭代器是指针的通用形式,但const_iteratoriterator还是两回事。 因此,我相信,我也无法使用const_cast<>const_iterator转换为iterator

一种方法是定义一个迭代器,该迭代器将移动直到const_iterator指向的元素。 但这似乎是线性时间算法。

关于实现此目标的最佳方法的任何想法?

aJ. asked 2020-08-05T11:58:15Z
9个解决方案
68 votes

C ++ 11中有一个解决方案,它具有恒定的时间复杂度:对于任何序列,关联或无序关联容器(包括所有标准库容器),都可以使用空范围调用range-erase成员函数:

template <typename Container, typename ConstIterator>
typename Container::iterator remove_constness(Container& c, ConstIterator it)
{
    return c.erase(it, it);
}

范围擦除成员函数具有一对const_iterator参数,但它们返回iterator。由于提供的是空范围,因此擦除调用不会更改容器的内容。

为此技巧向Howard Hinnant和Jon Kalb致谢。

James McNellis answered 2020-08-05T11:58:41Z
14 votes

不幸的是,线性时间是唯一的方法:

iter i(d.begin());
advance (i,distance<ConstIter>(i,ci));

其中iter和constIter是合适的typedef,而d是要在其上进行迭代的容器。

PaulJWilliams answered 2020-08-05T11:59:07Z
4 votes

在上一篇文章的答案中,有几人(包括我在内)出于非性能相关的原因而建议使用const_iterators。 从设计板到代码的可读性,可追溯性...使用const_iterators提供对非const元素的变异访问要比根本不使用const_iterators差得多。 您正在将代码转换为只有您自己会理解的代码,这会带来更糟糕的设计和真正的可维护性。 仅使用const将其抛弃比根本不使用const更糟糕。

如果您确定要使用它,则C ++的好/坏部分是,您总是可以得到足够的绳索来吊起自己。 如果您打算将const_iterator用于性能问题,则应该重新考虑一下,但是如果您仍然想摆脱困境,那么C ++可以提供选择的武器。

首先,最简单的方法是:如果您的操作将参数作为const使用(即使在内部应用const_cast),我相信它应该可以在大多数实现中直接运行(即使它可能是未定义的行为)。

如果您无法更改函子,则可以从任一方面解决问题:在const迭代器周围提供非const迭代器包装,或者在非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
};
David Rodríguez - dribeas answered 2020-08-05T11:59:46Z
4 votes

Scott Meyer关于优先于const_iterators的迭代器的文章回答了这一问题。 Visage的答案是唯一安全的C ++ 11之前的替代方案,但对于实施良好的随机访问迭代器而言,实际上是恒定时间,而对于其他方法则是线性时间。

Pontus Gagge answered 2020-08-05T12:00:07Z
3 votes

这可能不是您想要的答案,但是有些相关。

我假设您想更改迭代器指向的内容。 我做的最简单的方法是const_cast代替返回的引用。

像这样

const_cast<T&>(*it);

leiz answered 2020-08-05T12:00:36Z
2 votes

我认为,在设计良好的程序中无需进行此转换。

如果需要,请尝试重新设计代码。

作为解决方法,您可以下一步:

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

但是我认为有时这种转换是不可能的,因为您的算法无法访问容器。

bayda answered 2020-08-05T12:01:09Z
1 votes

您可以从const_iterator中减去begin()迭代器以获得const_iterator指向的位置,然后再将begin()添加回该位置以获得一个非const迭代器。 我认为这对于非线性容器不是非常有效,但是对于线性容器(例如矢量),这将需要固定的时间。

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;

编辑:这似乎仅适用于线性(随机访问)容器。

marcog answered 2020-08-05T12:01:33Z
0 votes

您可以将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;
Ankit answered 2020-08-05T12:01:53Z
0 votes

我认为,提出一个解决方案解决方案很有趣,该解决方案适用于不在标准库中并且不包含delete()方法的容器。

尝试使用此功能将导致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
user2813810 answered 2020-08-05T12:02:18Z
translate from https://stackoverflow.com:/questions/765148/how-to-remove-constness-of-const-iterator