Qt: создать изменяемый итератор для шаблона (карты, списки, наборы, ...)

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

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

Вы пробовали std::remove_if (container.begin(), container.end(), [](auto& x) { return x.value() == "X"});. И QMap, и QList предоставляют интерфейс итератора STL.

tmaric 17.01.2019 11:50

Я знаю, что есть remove_if, но реальный код более сложный. Например, контейнер остается неизменным или полностью очищается, если выполняются некоторые условия, которые я хочу проверить в той же функции.

Patrick Roocks 17.01.2019 12:13
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
7
2
262
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вы можете использовать СФИНАЕ, чтобы отключить шаблон функции для тех параметров 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().

Спасибо за вклад, но я действительно не понимаю, как это помогает. В Qt нет ":: mutable_iterator", я просто использовал его в качестве заполнителя - на этом этапе мне нужна некоторая "магия", которая автоматически переводит этот заполнитель в конструктор соответствующего Iterator (например, QMutableMapIterator, QMutableListIterator, .. .)

Patrick Roocks 17.01.2019 11:24

Хм, значит, я не понял твоего вопроса. Вы пытаетесь построить итератор, определив его тип с помощью typename. Можете ли вы сконструировать это как возврат из функции-члена контейнера begin? auto iterator = container.begin();? Каковы условия на итераторе, чтобы иметь функцию-член remove()?

tmaric 17.01.2019 11:28
Ответ принят как подходящий

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

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

Другие вопросы по теме