Подтвердить что ты не робот

Как удалить константу const_iterator?

В качестве дополнения к этому вопросу Являются ли const_iterators быстрее?, у меня есть еще один вопрос о const_iterators. Как удалить константу const_iterator? Хотя итераторы являются обобщенной формой указателей, но все же const_iterator и iterator - две разные вещи. Поэтому, я считаю, я также не могу использовать const_cast<> для скрытых от const_iterator до iterator s.

Один подход может заключаться в том, что вы определяете итератор, который перемещается до уровня, на который указывает const_iterator. Но это выглядит как алгоритм линейного времени.

Любая идея о том, что это лучший способ достичь этого?

4b9b3361

Ответ 1

В С++ 11 есть решение с постоянной временной сложностью: для любой последовательности, ассоциативного или неупорядоченного ассоциативного контейнера (включая все контейнеры стандартной библиотеки) вы можете вызвать функцию члена-стирания с пустым диапазоном

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

Функции члена-стирания имеют пару параметров const_iterator, но они возвращают iterator. Поскольку предоставляется пустой диапазон, вызов стирания не изменяет содержимое контейнера.

Совет шляпы Говарду Хиннанту и Джону Калбу за этот трюк.

Ответ 2

К сожалению, линейное время - единственный способ сделать это:

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

где iter и constIter являются подходящими typedefs, а d - контейнер, над которым вы выполняете итерацию.

Ответ 3

В ответах на ваш предыдущий пост было несколько человек, включая меня, которые рекомендовали использовать константы-константы вместо причин, не связанных с производительностью. Читаемость, прослеживаемость от проектной платы до кода... Использование const_iterators для предоставления мутирующего доступа к неконстантическому элементу намного хуже, чем никогда не использовать const_iterators. Вы превращаете свой код во что-то, что только вы поймете, с худшим дизайном и реальной ремонтопригодностью. Использование const просто для его удаления намного хуже, чем не использовать const вообще.

Если вы уверены, что хотите этого, хорошая/плохая часть С++ заключается в том, что вы всегда можете получить достаточно веревки, чтобы повесить себя. Если ваше намерение использует const_iterator для проблем с производительностью, вы должны действительно переосмыслить его, но если вы все еще хотите снять свою ногу... хорошо, С++ может предоставить вам свое оружие.

Во-первых, самое простое: если ваши операции принимают аргументы как const (даже если они применяются внутри const_cast), я считаю, что он должен работать непосредственно в большинстве реализаций (даже если это, вероятно, поведение undefined).

Если вы не можете изменить функторы, вы можете решить проблему с любой стороны: предоставить не-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
};

Ответ 4

Возможно, это не тот ответ, который вам нужен, но несколько связанный.

Я предполагаю, что вы хотите изменить то, на что указывает итератор. Самый простой способ, который я делаю, это const_cast вместо возвращаемой ссылки.

Что-то вроде этого

const_cast<T&>(*it);

Ответ 5

статья Скотта Мейера о предпочтении итераторов над const_iterators отвечает на это. Визуальный ответ является единственной безопасной альтернативой pre-С++ 11, но на самом деле является постоянным временем для хорошо реализованных итераторов произвольного доступа и линейного времени для других.

Ответ 6

Я считаю, что это преобразование не требуется в хорошо разработанной программе.

Если вам это нужно - попробуйте перепроектировать код.

В качестве обходного пути вы можете сделать следующее:

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

Но я думаю, что иногда это преобразование невозможно, потому что ваши алгоритмы не могут иметь доступ к контейнеру.

Ответ 7

Вы можете вычесть итератор begin() из const_iterator, чтобы получить позицию, на которую указывает 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;

РЕДАКТИРОВАТЬ. Это работает только для линейных (произвольный доступ) контейнеров.

Ответ 8

вы можете преобразовать указатель значения константного итератора в указатель не const value и использовать его прямо что-то вроде этого

    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;

Ответ 9

Я подумал, что было бы здорово придумать решение для этого, которое работает для контейнеров, которые не входят в стандартную библиотеку, и не включают метод 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

Ответ 10

У меня нет (хорошего) ответа, но предложенный ответ  шаблон typename Container:: iterator remove_constness (Container & c, ConstIterator it)

не работает для std:: forward_list (потому что у него нет метода стирания).

Подход std:: advance() явно работает, но при ужасной стоимости.