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





Вы можете использовать СФИНАЕ, чтобы отключить шаблон функции для тех параметров 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, .. .)
Хм, значит, я не понял твоего вопроса. Вы пытаетесь построить итератор, определив его тип с помощью typename. Можете ли вы сконструировать это как возврат из функции-члена контейнера begin? auto iterator = container.begin();? Каковы условия на итераторе, чтобы иметь функцию-член remove()?
Вы можете определить шаблон признаков и частично специализироваться для соответствующих контейнеров.
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();
}
}
Вы пробовали
std::remove_if (container.begin(), container.end(), [](auto& x) { return x.value() == "X"});. ИQMap, иQListпредоставляют интерфейс итератора STL.