如何在基于范围的for循环中很好地解包unordered_map元素
How to nicely unpack unordered_map element in range-based for loop?
考虑以下代码:
#include <tuple>
#include <string>
#include <iostream>
#include <unordered_map>
int main()
{
std::unordered_map<int,std::string> map {{5,"five"},{1,"one"},{-7,"minus seven"}};
int x;
std::string s;
for(std::tie(x,s) : map)
std::cout << x << "->" << s << "n";
}
在for
的行中,我得到clang++-3.6的错误:
test.cpp:14:23: error: for range声明必须声明一个变量
这似乎意味着,如果我打开for(auto val: map)
, val
将是std::tie
的解包,那么CC_2就没有运气了。
但是我真的想在循环体中没有任何额外的行就自动解包这对。还有什么优雅的方法可以做到这一点吗?
目前最好的方法是使用c++ 17中出现的结构化绑定,如下面的代码所示。
#include <tuple>
#include <string>
#include <iostream>
#include <unordered_map>
int main()
{
std::unordered_map<int,std::string> map {{5,"five"},{1,"one"},{-7,"minus seven"}};
for(const auto& [x,s] : map)
std::cout << x << " -> " << s << "n";
}
几个想法。第一个示例需要额外的一行示例来构造一对迭代器,并使用while
循环:
template <typename I, typename... Ts>
bool for_tie(std::pair<I,I>& its, Ts&&... parts)
{
if (its.first == its.second) return false;
std::tie(std::forward<Ts>(parts)...) = *its.first++;
return true;
}
int main()
{
std::unordered_map<int,std::string> map {{5,"five"},{1,"one"},{-7,"minus seven"}};
int x;
std::string s;
auto map_its = std::make_pair(begin(map), end(map));
while(for_tie(map_its, x, s))
std::cout << x << "->" << s << "n";
}
下一个有一个类似于标准库算法的API,这里使用lambda:
#include <experimental/tuple>
template <typename I, typename F>
void for_tie(I it, I end, F func)
{
for (; it != end; ++it) std::experimental::apply(func, *it);
}
int main()
{
std::unordered_map<int,std::string> map {{5,"five"},{1,"one"},{-7,"minus seven"}};
for_tie(begin(map), end(map), [](int x, const std::string &s) {
std::cout << x << "->" << s << "n";
});
}
std::experimental::apply
是很容易实现自己,如果你没有访问库基础TS的实现。
这个想法是,使用特殊的模板类map_unpacker
,它不应该在任何意义上完成,只是为了显示这个想法:
#include <tuple>
#include <string>
#include <iostream>
#include <unordered_map>
#include <tuple>
#include <algorithm>
template<typename T1, typename T2>
struct map_unpacker {
typedef typename std::unordered_map<T1,T2> convert_map;
typedef typename convert_map::const_iterator convert_iterator;
typedef typename convert_map::value_type pair;
struct iterator : convert_iterator {
iterator( const map_unpacker &u, convert_iterator it ) : unpack( u ), convert_iterator( it ) {}
pair operator*() { auto &&p = convert_iterator::operator*(); unpack.ref1 = p.first; unpack.ref2 = p.second; return p; }
const map_unpacker &unpack;
};
map_unpacker( const std::unordered_map<T1,T2> &m, T1 &r1, T2 &r2 ) : map( m ), ref1( r1 ), ref2( r2 ) {}
iterator begin() const { return iterator( *this, map.begin() ); }
iterator end() const { return iterator( *this, map.end() ); }
private:
const std::unordered_map<T1,T2> ↦
T1 &ref1;
T2 &ref2;
};
int main()
{
std::unordered_map<int,std::string> map {{5,"five"},{1,"one"},{-7,"minus seven"}};
int x;
std::string s;
for( auto &&v : map_unpacker<int,std::string>( map, x, s ) ) {
std::cout << "x==" << x << " s==" << s << std::endl;
}
}
好的,既然您喜欢语法糖,并且可能不太关心性能,那么我想到的这个想法如何:
构建自己的类,封装map并返回自己的迭代器,迭代器在每次迭代器加1时保存x和s中的值。
下面的代码可能不是我写过的最好的,但它解决了你的问题,满足了你的约束,你可以进一步改进。
class mai_tie
{
public:
class iterator
{
public:
iterator(std::unordered_map<int, std::string>::iterator& pos, std::unordered_map<int, std::string>::iterator& end, int& x, std::string& s)
: _pos(pos), _end(end), _x(&x), _s(&s)
{
*_x = pos->first;
*_s = _pos->second;
}
iterator(std::unordered_map<int, std::string>::iterator pos) : _pos(pos), _end(pos)
{}
bool operator==(const iterator& rhs)
{ return _pos == rhs._pos; }
bool operator!=(const iterator& rhs)
{ return _pos != rhs._pos; }
void operator++()
{
++_pos;
if (_pos != _end && _x && _s)
{
*_x = _pos->first;
*_s = _pos->second;
}
}
std::pair<int, std::string> operator*()
{
return std::make_pair(_pos->first, _pos->second);
}
private:
std::unordered_map<int, std::string>::iterator _pos;
const std::unordered_map<int, std::string>::iterator _end;
int* _x = nullptr;
std::string* _s = nullptr;
};
mai_tie(std::unordered_map<int, std::string>& map, int& x, std::string& s) : _map(map), _x(x), _s(s) {};
iterator begin()
{
return iterator(_map.begin(), _map.end(), _x, _s);
}
iterator end()
{
return iterator(_map.end());
}
private:
std::unordered_map<int, std::string>& _map;
int& _x;
std::string& _s;
};
你可以这样使用:
std::unordered_map<int, std::string> map{ { 5, "five" }, { 1, "one" }, { -7, "minus seven" } };
int x;
std::string s;
for (auto z : mai_tie(map, x, s))
std::cout << x << "->" << s << "n";
原谅我这个班的名字,我今天觉得"滑稽"。
还有一个个人建议:不要使用这个类。没有人会知道你的代码里发生了什么。编写规则的、简单的代码。省一行字是不值得的。你之后的下一个程序员会很感激(从经验来看,已经调试了很多代码,人们做了这样"聪明"的事情来节省几行)。
可以这样修改for循环:
for(const auto& ele: map)
std::cout<< ele.first<<"->"<<ele.second<<"n";
编辑
for(const auto& x : map)
{
std::tie(y,s)=x;
std::cout << y << "->" << s << "n";
}
相关文章:
- 我编写了代码将十进制分数转换为其二进制等效数.它编译得很好,但在执行时挂起
- 很好的语法来获取对向量/数组数据的大小引用?
- 我认为我的代码很好,但它在 cin a 之后停止并且没有进一步?
- 尽管一切看起来都很好,但值不会交换
- 为什么不同类型层次结构的指针之间的dynamic_cast定义得很好?
- 在 C++11 中利用 int*_t、int_fast*_t 和 int_least*_t 之间的差异的一个很好的例子是
- 对于短字符串来说,这是一个很好的哈希函数吗?
- 为什么指标有时效果很好,有时效果不佳?写下霍夫曼代码
- Red Hat:使用<atomic>编译很好,但链接器找不到__atomic_store_16;什么库?
- 使用移位的无符号数字作为数组的索引号是一种很好的做法
- 是否很好地使用状态模式来维护当前选定的对象?
- C++|以一种很好的方式将树(不一定是二进制的)打印到stdout
- 静态库单独使用很好,但在引用时会抛出错误
- 如果验证容器的大小并在同一条件语句下访问元素,这是很好的做法吗?
- 可变参数函数模板不能很好地使用 std::function 作为参数
- 有没有办法关闭文件? fclose 不能很好地工作
- 引用的静态强制转换强制模板实例化,其中不完整的类型很好
- libc++:为什么流关闭后仍然很好
- 不能很好地运行HElib,但它建立在Windows 10 x64上
- 接口在C++中是一种很好的做法吗?