Суть второй реализации - это то, что вы не скопировали: чтобы было проще реализовать определенное требование. А именно, iterator
должен быть неявно конвертируемым в const_iterator
.
. Трудность здесь заключается в сохранении тривиального копируемости. Если бы вы сделали это:
template<class T>
class ContainerIterator {
using pointer = T*;
using reference = T&;
...
ContainerIterator(const ContainerIterator &) = default; //Should be trivially copyable.
ContainerIterator(const ContainerIterator<const T> &) {...}
};
Это не сработает. const const Typename
разрешается до const Typename
. Поэтому, если вы создаете экземпляр ContainerIterator
с помощью const T
, то теперь у вас есть два конструктора с одинаковой сигнатурой, один из которых по умолчанию. Что ж, это означает, что компилятор будет игнорировать ваши значения по умолчанию для конструктора копирования и, таким образом, использовать вашу нетривиальную реализацию конструктора копирования.
Это плохо.
Есть способы избежать этого, используя некоторыеИнструменты метапрограммирования для определения константности T
, но самый простой способ исправить это - указать константу в качестве параметра шаблона:
template<class T, bool IsConst>
class ContainerIterator {
using pointer = std::conditional_t<IsConst, const T*, T*>;
using reference = std::conditional_t<IsConst, const T&, T&>;
...
ContainerIterator(const ContainerIterator &) = default; //Should be trivially copyable.
template<bool was_const = IsConst, class = std::enable_if_t<IsConst || !was_const>>>
ContainerIterator(const ContainerIterator<T, was_const> &) {...}
};
Шаблоны никогда не считаются конструкторами копирования, так что это не помешает тривиальному копируемости. При этом также используется SFINAE для исключения конструктора преобразования в том случае, если он не является итератором const
.
Более подробную информацию об этом шаблоне можно найти также .