【问题标题】:Custom class range based for-loop over a 2D map基于自定义类范围的 2D 地图循环
【发布时间】:2022-12-21 09:38:46
【问题描述】:

我有以下类 MyClass,其中包含 2D 地图 (std::map<std::string, std::map<std::string,double>>)。

我想知道是否可以为 MyClass 实现 MyClass::begin() MyClass::end() 函数,以便有一个基于范围的 for 循环(如下面的代码所示),这将允许我打印包含在那张二维地图。

明确地说,我不想引入双 for 循环, 我想要一个 for() 循环
(之后的目标是将地图 map 作为 MyClass 的私有成员,并且只允许通过基于范围的 for 循环遍历类)

提前谢谢了!


class MyClass {
public:
    MyClass(){};
    ~MyClass(){}; 
    std::map<std::string, std::map<std::string,double>> map = {};
};


int main()
{
    MyClass myClass; 
    myClass.map["a"]["a"] = 1;
    // ...
    myClass.map["e"]["c"] = 12;

    for (std::pair<const std::string, double> &obj : mycls){
        std::cout << "obj.second = " << obj.second << std::endl; 
  }
  return 0; 
}

【问题讨论】:

  • 简短的回答是肯定的,这是可能的。
  • @sweenish 谢谢你知道我如何实现它吗?如果我问是因为我不知道如何实施提前谢谢
  • 您需要为您的类提供迭代器。它们可能只是地图迭代器的包装器。
  • 这是一个半高级的话题。我们可以为您提供现成的实施或答案中的教程。两者都不适合作为 StackOverflow 的答案。你知道如何实现自定义迭代器吗?如果是,那么什么确切地是你的问题吗?如果不是,那么第一步就是通过搜索有关该主题的教程来学习如何这样做。
  • @Fureeish 如果我有一个一维地图或一维向量,我知道如何“实现”它只是 mp.begin() 和 mp.end() 但是对于二维(或更普遍的多维情况)我不知道如何它可以实施在问这个问题之前,我什至不知道是否可以实施。我可能不明白事情只是因为我不知道存在这样的功能所以根据你的回答我首先需要了解如何实现我自己的迭代器并且一旦我实现了迭代器我可以定义开始结束函数?我对吗 ?谢谢

标签: c++ for-loop c++11 range-based-loop


【解决方案1】:

是的,这是可以做到的。您必须引入迭代器功能。

如果您查看 CPP 参考 here,那么您可以在解释中看到基于范围的 for 循环的要求。只需要 5 个函数。

  • 您的类必须有一个返回自定义迭代器的 begin() 函数

  • 您的类必须有一个返回自定义迭代器的 end() 函数

  • 您的类必须有一个自定义迭代器和一个用于取消引用它的函数

  • 您的类必须有一个自定义迭代器和一个用于递增它的函数

  • 您的类必须有一个自定义迭代器和一个用于比较的函数

自定义迭代器的实现非常简单。

您编写 5 条 using 语句来满足正式要求。然后,您需要定义迭代器的内部表示,例如指针或其他迭代器。

在下面的示例中,我们重用了嵌套映射中的 2 个迭代器。外部地图的迭代器和内部地图的迭代器。为了更容易实现功能,我们还将存储指向周围自定义类的指针。

递增(和递减操作)需要付出更多的努力,因为我们需要处理内部映射迭代器的回绕。

示例:如果内部迭代器到达末尾,我们必须递增外部迭代器,然后将内部迭代器重置为开始位置。

我还添加了一个面临类似挑战的递减函数。

通过添加差异函数,我通过破坏字符串与双精度数的关系,使整个事情变得可排序。所以,不要那样做。

但是有了这个函数,我们可以很容易地实现像宇宙飞船一样的迭代器比较。

请参阅下面的许多潜在解决方案之一。为了您的方便,我添加了更多功能。

#include<string> 
#include<map> 
#include<iostream>
#include <iterator>
#include <algorithm>
#include <numeric>

using Map = std::map<std::string, double>;
using MapMap = std::map<std::string, Map>;

struct MyClass {
    struct iterator; // Forward declaration

    MapMap mp{};

    bool empty() { return mp.empty(); }
    size_t size() { return std::accumulate(mp.begin(), mp.end(), 0u, [](const size_t& sum, const auto& m) { return sum + m.second.size(); }); }

    iterator begin() { return iterator(&mp, mp.begin()->second.begin(), mp.begin()); }
    iterator end() { return iterator(&mp, ((--mp.end())->second).end(), mp.end()); }

    // iterator stuff ---------------------------------------------------------------------------
    struct iterator {
        // Definitions ----------------
        using iterator_category = std::bidirectional_iterator_tag;
        using difference_type = std::ptrdiff_t;
        using value_type = double;
        using pointer = double*;
        using reference = double&;

        // Data
        MapMap* outerMapPtr{};               // Reference to surrounding nested map
        MapMap::iterator outerMapIterator{}; // Iterator to the outer part of the nesed map
        Map::iterator innerMapIterator{};    // Iterator to the outer part of the nesed map

        // Constructor for iterator. Take a pointer to the surrounding nested map and both iterators
        iterator(MapMap* const mmSD, const Map::iterator& msdIter, const MapMap::iterator& mmsdIter) :
            outerMapPtr(mmSD), innerMapIterator(msdIter), outerMapIterator(mmsdIter) {}

        // Dereferencing
        reference operator *() const { return innerMapIterator->second; }
        reference operator->() const { return **this; }

        // Comparison. Must be template, because the other iterator may be of different type
        template <typename Iter>
        bool operator != (const Iter& other) const { return(outerMapIterator != other.outerMapIterator) or (innerMapIterator != other.innerMapIterator); }
        template <typename Iter>
        bool operator == (const Iter& other) const { return(outerMapIterator == other.outerMapIterator) and (innerMapIterator == other.innerMapIterator); }

        bool operator < (const iterator& other) const { return other - *this < 0; };
        bool operator <= (const iterator& other) const { return other - *this <= 0; };
        bool operator > (const iterator& other) const { return other - *this > 0; };
        bool operator >= (const iterator& other) const { return other - *this >= 0; };

        // Arithmetic operations
        // A little bit complex
        iterator operator ++() {
            // If we are at the end with the outer iterator, we do nothing
            if (outerMapPtr->empty() or (outerMapIterator != outerMapPtr->end())) {

                // We want to increment the inner iterator. Before we do that, we check, if this is already at the end
                if (innerMapIterator != outerMapIterator->second.end())
                    ++innerMapIterator;

                // So, now the innerMapIterator can be at the end, either from the beginning or after incrementing it
                if (innerMapIterator == outerMapIterator->second.end()) {

                    // Increment outer iterator
                    ++outerMapIterator;

                    // And reset the inner interator back to begin, but only if the outer iterator is not at the end now
                    if (outerMapIterator != outerMapPtr->end())
                        innerMapIterator = outerMapIterator->second.begin();
                }
            }
            return *this;
        }
        iterator operator --() {
            // No decrementation on empty container
            if (not outerMapPtr->empty()) {

                // If we are at the end of the outer iterator then decrement it
                if (outerMapIterator == outerMapPtr->end())
                    --outerMapIterator;

                // If we are not at the begin the inner iterator
                if (innerMapIterator != outerMapIterator->second.begin())
                    --innerMapIterator;
                else {
                    // Inner iterator was at begin, therefore also decrement outer one
                    if (outerMapIterator != outerMapPtr->begin()) {
                        --outerMapIterator;
                        innerMapIterator = outerMapIterator->second.end();
                        if (innerMapIterator != outerMapIterator->second.begin())
                            --innerMapIterator;
                    }
                }
            }
            return *this;
        }

        // Derived functions
        iterator operator++(int) { iterator tmp = *this; ++* this; return tmp; }
        iterator operator--(int) { iterator tmp = *this; --* this; return tmp; }
        iterator operator +(const difference_type& n) const {
            iterator temp{ *this };  difference_type k{ n }; if (k > 0) while (k--)++temp; else while (k++)--temp; return temp;
        }
        iterator operator +=(const difference_type& n) {
            difference_type k{ n }; if (k > 0) while (k--)++* this; else while (k++)--* this; return *this;
        };
        iterator operator -(const difference_type& n) const {
            iterator temp{ *this };  difference_type k{ n }; if (k > 0) while (k--)--temp; else while (k++)++temp; return temp;
        }
        iterator operator -=(const difference_type& n) {
            difference_type k{ n }; if (k > 0) while (k--)--* this; else while (k++)++* this; return *this;
        };

        // Difference. Very inefficient
        difference_type operator-(const iterator& other) const {

            difference_type result{};
            // No subtraction on empty container
            if (not outerMapPtr->empty()) {

                int indexThis{ }, indexOther{ }, index{};

                iterator current(outerMapPtr, outerMapPtr->begin()->second.begin(), outerMapPtr->begin());
                iterator last(outerMapPtr, ((--outerMapPtr->end())->second).end(), outerMapPtr->end());

                for (; current != last; ++current, ++index) {
                    if (current  == *this)
                        indexThis = index;
                    if (current == other)
                        indexOther = index;
                }
                if (current == *this)
                    indexThis = index;
                if (current == other)
                    indexOther = index;

                if (indexThis >= 0 and indexOther >= 0)
                    result = indexThis - indexOther;
            }
            return result;
        }
    };
};

int main()
{
    MyClass mycls;
    mycls.mp["a"]["a"] = 1;
    mycls.mp["a"]["b"] = 2;
    mycls.mp["a"]["c"] = 3;
    mycls.mp["b"]["a"] = 4;
    mycls.mp["b"]["b"] = 5;
    mycls.mp["b"]["c"] = 6;
    mycls.mp["d"]["a"] = 7;
    mycls.mp["d"]["b"] = 8;
    mycls.mp["d"]["c"] = 9;
    mycls.mp["e"]["a"] = 10;
    mycls.mp["e"]["b"] = 11;
    mycls.mp["e"]["c"] = 12;

    std::cout << "
Size: " << mycls.size() << "

";

    for (double d : mycls)
        std::cout << d << ' ';

    return 0;
}

【讨论】:

    猜你喜欢
    • 2017-07-02
    • 2018-12-14
    • 1970-01-01
    • 2015-11-03
    • 2016-10-31
    • 1970-01-01
    • 1970-01-01
    • 2014-02-10
    • 1970-01-01
    相关资源
    最近更新 更多