Qt: construct a mutable iterator for template (maps, lists, sets, …)

人盡茶涼 提交于 2021-02-18 11:29:45

问题


In my code I often have functions doing the same thing on different iterable Qt Container types, for instance:

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

(and I know there is already a removeAll function in QList. This is just a silly minimal example)

The actual code is more complex and hence this introduces a lot of code duplication. I would prefer to have something like:

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

Of course, this doesn't compile, as there is simply no "::mutable_iterator" type definition within Qt. Could one construct one? I don't see a easy way to so. A function like "template<...> getMyMutableIterator" can't work in this case, as we are not allowed to return different types for an overloaded function.

But there is a plenty of new "template magic" from C++17 which I have not really understood yet. I could imagine that it could be a easy way to realize the above code. Has anyone a solution to reduce the code duplication here?


回答1:


You can define a traits template, and partially specialise for the appropriate containers

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

You then use q_container_traits<T> in your function.

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



回答2:


You could use SFINAE to disable a function template for those parameters T that don't have the mutable_iterator type using std::enable_if_t and 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();
    }
}

This reads something like: enable the void type as the return type of the function if T::mutable_iterator type is the same as mutable_iterator. If T doesn't have mutable_iterator, the substitution fails.

You can use type_traits to adapt this to other conditions on T, or its iterator, like check if it has the remove() member function.



来源:https://stackoverflow.com/questions/54232760/qt-construct-a-mutable-iterator-for-template-maps-lists-sets

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!