在 2D 地图上自定义 class 基于范围的 for 循环
Custom class range based for-loop over a 2D map
我有以下 class MyClass
,其中包含 2D 地图 (std::map<std::string, std::map<std::string,double>>
)。
我想知道是否可以为 MyClass
实现 MyClass::begin()
MyClass::end()
函数,以便有一个基于范围的 for 循环(如下面的代码所示) ) 这将允许我打印该二维地图中包含的所有双打。
明确地说,我不想引入双循环,
我想要一个 for() 循环
(之后的目标是将映射 mp
作为 MyClass
的私有成员,并且只允许通过基于范围的 for 循环遍历 class)
非常感谢!
#include<string>
#include<map>
#include<iostream>
class MyClass{
public:
MyClass() {} ;
~MyClass(){};
std::map<std::string, std::map<std::string,double>> mp = {};
};
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;
for (std::pair<const std::string, double> &obj : mycls){
std::cout << "obj.second = " << obj.second << std::endl;
}
return 0 ;
}
是的,可以做到。您必须引入迭代器功能。
如果您查看 CPP 参考资料 here,那么您可以在解释中看到基于范围的 for 循环的要求。只需要5个功能。
你的 class 必须有一个 begin()
函数,returns 一个自定义迭代器
您的 class 必须有一个 end()
函数,returns 一个自定义迭代器
您的 class 必须有一个自定义迭代器和一个用于取消引用它的函数
您的 class 必须有一个自定义迭代器和一个用于递增它的函数
您的 class 必须有一个自定义迭代器和一个比较函数
自定义迭代器的实现非常简单。
你写了 5 条 using 语句来满足形式要求。然后,您需要定义迭代器的内部表示,例如指针或其他迭代器。
在下面的示例中,我们重用了嵌套映射中的 2 个迭代器。外部地图的迭代器和内部地图的迭代器。为了更容易实现功能,我们还将存储指向周围自定义 class.
的指针
递增(和递减操作)需要更多的努力,因为我们需要处理内部映射迭代器的回绕。
示例:如果内部迭代器到达末尾,我们必须递增外部迭代器,然后将内部迭代器重置为开始位置。
我还添加了一个面临类似挑战的递减函数。
通过添加差异函数,我通过破坏字符串与双精度数的关系,使整个事情变得可排序。所以,不要那样做。
但是有了那个函数,我们可以很容易地实现 space 像迭代器比较一样的运送。
请参阅以下众多可能的解决方案之一。为了您的方便,我添加了更多功能。
#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 << "\nSize: " << mycls.size() << "\n\n";
for (double d : mycls)
std::cout << d << ' ';
return 0;
}
我有以下 class MyClass
,其中包含 2D 地图 (std::map<std::string, std::map<std::string,double>>
)。
我想知道是否可以为 MyClass
实现 MyClass::begin()
MyClass::end()
函数,以便有一个基于范围的 for 循环(如下面的代码所示) ) 这将允许我打印该二维地图中包含的所有双打。
明确地说,我不想引入双循环,
我想要一个 for() 循环
(之后的目标是将映射 mp
作为 MyClass
的私有成员,并且只允许通过基于范围的 for 循环遍历 class)
非常感谢!
#include<string>
#include<map>
#include<iostream>
class MyClass{
public:
MyClass() {} ;
~MyClass(){};
std::map<std::string, std::map<std::string,double>> mp = {};
};
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;
for (std::pair<const std::string, double> &obj : mycls){
std::cout << "obj.second = " << obj.second << std::endl;
}
return 0 ;
}
是的,可以做到。您必须引入迭代器功能。
如果您查看 CPP 参考资料 here,那么您可以在解释中看到基于范围的 for 循环的要求。只需要5个功能。
你的 class 必须有一个
begin()
函数,returns 一个自定义迭代器您的 class 必须有一个
end()
函数,returns 一个自定义迭代器您的 class 必须有一个自定义迭代器和一个用于取消引用它的函数
您的 class 必须有一个自定义迭代器和一个用于递增它的函数
您的 class 必须有一个自定义迭代器和一个比较函数
自定义迭代器的实现非常简单。
你写了 5 条 using 语句来满足形式要求。然后,您需要定义迭代器的内部表示,例如指针或其他迭代器。
在下面的示例中,我们重用了嵌套映射中的 2 个迭代器。外部地图的迭代器和内部地图的迭代器。为了更容易实现功能,我们还将存储指向周围自定义 class.
的指针递增(和递减操作)需要更多的努力,因为我们需要处理内部映射迭代器的回绕。
示例:如果内部迭代器到达末尾,我们必须递增外部迭代器,然后将内部迭代器重置为开始位置。
我还添加了一个面临类似挑战的递减函数。
通过添加差异函数,我通过破坏字符串与双精度数的关系,使整个事情变得可排序。所以,不要那样做。
但是有了那个函数,我们可以很容易地实现 space 像迭代器比较一样的运送。
请参阅以下众多可能的解决方案之一。为了您的方便,我添加了更多功能。
#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 << "\nSize: " << mycls.size() << "\n\n";
for (double d : mycls)
std::cout << d << ' ';
return 0;
}