Qt: создайте изменяемый итератор для шаблона (карты, списки, наборы, ...) - PullRequest
0 голосов
/ 17 января 2019

В моем коде у меня часто есть функции, выполняющие одно и то же на разных итерируемых типах Qt Container, например:

void removeX(QMap<qint64, QString> & map)
{
    QMutableMapIterator<qint64, QString> it(map);
    while (it.hasNext()) {
        it.next();
        if (it.value() == "X") it.remove();
    }
}

void removeX(QList<QString> & list)
{
    QMutableListIterator<QString> it(list);
    while (it.hasNext()) {
        it.next();
        if (it.value() == "X") it.remove();
    }
}

(и я знаю, что в QList уже есть функция removeAll. Это просто глупый минимальный пример)

Фактический код является более сложным, и, следовательно, в нем много дублирования кода. Я бы предпочел иметь что-то вроде:

template <typename T>
void removeX_(T & container)
{
    typename T::mutable_iterator it(container);
    while (it.hasNext()) {
        it.next();
        if (it.value() == "X") it.remove();
    }
}

Конечно, это не компилируется, так как в Qt просто нет определения типа ":: mutable_iterator". Можно ли построить один? Я не вижу простой способ сделать это. Такая функция, как «template <...> getMyMutableIterator», не может работать в этом случае, поскольку нам не разрешено возвращать различные типы для перегруженной функции.

Но есть много новой "магии шаблонов" из C ++ 17, которую я до сих пор не понял. Я мог представить, что это может быть простой способ реализовать приведенный выше код. У кого-нибудь есть решение, чтобы уменьшить дублирование кода здесь?

Ответы [ 2 ]

0 голосов
/ 17 января 2019

Вы можете определить шаблон признаков и частично специализироваться для соответствующих контейнеров

template <typename Container> struct q_container_traits;

template <typename T> struct q_container_traits<QList<T>>
{
    using mutable_iterator = QMutableListIterator<T>;
    using const_iterator = QListIterator<T>;
};

template <typename Key, typename Value> struct q_container_traits<QMap<Key, Value>>
{
    using mutable_iterator = QMutableMapIterator<Key, Value>;
    using const_iterator = QMapIterator<Key, Value>;
};

// etc

Затем вы используете q_container_traits<T> в своей функции.

template <typename T>
void removeX(T & container)
{
    typename q_container_traits<T>::mutable_iterator it(container);
    while (it.hasNext()) {
        it.next();
        if (it.value() == "X") it.remove();
    }
}
0 голосов
/ 17 января 2019

Вы можете использовать SFINAE , чтобы отключить шаблон функции для тех параметров T, которые не имеют типа mutable_iterator, используя std::enable_if_t и std::is_same

template <typename T>
std::enable_if_t
<
    std::is_same_v
    <
        typename T::mutable_iterator, 
        mutable_iterator
    >, 
    void
> 
removeX_(T & container)
{
    typename T::mutable_iterator it(container);
    while (it.hasNext()) {
        it.next();
        if (it.value() == "X") it.remove();
    }
}

Это выглядит примерно так: включите тип void в качестве возвращаемого типа функции, если тип T::mutable_iterator совпадает с mutable_iterator. Если T не имеет mutable_iterator, замена завершится неудачно.

Вы можете использовать type_traits, чтобы адаптировать это к другим условиям на T, или его итератору, например , чтобы проверить, имеет ли он remove() функцию-члена .

...