преобразование const пользовательского итератора c выполнено правильно

#c #iterator #containers #std

#c #итератор #контейнеры #std

Вопрос:

Я пытаюсь реализовать конструктор копирования моего пользовательского итератора, совместимого с std, для пользовательского контейнера. Контейнер выглядит примерно так:

 template <typename T, Alloc>
class container {
    template <typename ValueType>
    class raw_iterator;
    
    ...
    using value_type = T;
    ...

    using iterator = raw_iterator<value_type>
    using const_iterator = raw_iterator<const value_type>
    ...
}
  

raw_iterator выглядит примерно так:

 template <typename T, Alloc>
template <typename ValueType>
class container<T, Alloc>::raw_iterator {
    ...
}
  

Если я прав, я должен реализовать конструктор копирования для обоих, итератора и const_iterator, так что итераторы могут быть скопированы в один и тот же тип, а обычный итератор может быть скопирован в const_iterator .
Как этого можно достичь?

PS: Я должен предоставить некоторую функциональность в конструкторе копирования, поэтому я не могу использовать сгенерированный implizit конструктор.

Комментарии:

1. Вы можете создать неявный конструктор для const_iterator , который принимает iterator аргумент as . Тогда все будет «просто работать».

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

3. Хорошо, позвольте мне попробовать привести пример.

Ответ №1:

Вы можете создать неявный конструктор для const_iterator, который принимает итератор в качестве аргумента. Тогда все будет «просто работать».

Вот пример того, что, я думаю, вы хотите:

 #include <type_traits>

template <typename ValueType>
class raw_iterator {
  public:
    // Typedefs
    using non_const_value_type = std::remove_const_t<ValueType>;
    using const_value_type = std::add_const_t<non_const_value_type>;

    // Constructor - Need extra template argument here to allow SFINAE to work.
    template <class U = ValueType,
      std::enable_if_t<std::is_same<U, const_value_type>::value, int> = 0>
    raw_iterator (raw_iterator<non_const_value_type> const amp; other) {
      // Do stuff.
    }

  private:
    // Friends - Make raw_iterator<X> friend of raw_iterator<X const>
    friend std::conditional_t<
        std::is_same<ValueType, non_const_value_type>::value,
        raw_iterator<const_value_type>, void
      >;
};
  

Существует условный конструктор, принимающий raw_iterator<AnythingNonConst> значение, которое существует только в том случае, если raw_iterator собственный тип temple равен AnythingConst .

Кроме того, raw_iterator<AnythingNonConst> является другом raw_iterator<AnythingConst> , но не наоборот. Таким образом, вы можете копировать любые элементы, которые вы хотите, в условный конструктор.

Вот онлайн-пример: https://wandbox.org/permlink/8gDzHyheIrpsTL5y .

Комментарии:

1. perfekt, значит, std::enable_if используется для включения / отключения правильного конструктора на основе ValueType ?

2. Да, для этого используется SFINAE. Я не могу адекватно объяснить это в комментарии, но вот отправная точка, если вы хотите узнать больше: en.wikipedia.org/wiki/Substitution_failure_is_not_an_error . Об этом также есть множество статей в блоге. Это хороший трюк, который очень часто пригодится!

3. По сути, when U != const_value_type , then std::enable_if_t — это typedef для типа, который на самом деле не существует в std::enable_if классе. И из-за SFINAE это не ошибка, вместо этого функция просто игнорируется компилятором. В этом случае только тогда, когда U == const_value_type typedef существует, и в этом случае компилятор учитывает этот конструктор.

4. Это действительно в c std или это реализация компилятора? Я думаю, если это шаблон, он действителен в std…

5. Работает для всех компиляторов C , это очень «стандартный» способ выполнения действий. Мой пример кода предназначен для C 14, но вы можете легко заставить его работать и для C 11, просто нужно добавить несколько typename s здесь и там.