Jam*_*rgy 26 c++ iteration dictionary readability c++11
我不知道该搜索什么.我发现重命名地图迭代器的第一个和第二个,但它不是我想要做的.
这是我想要做的[见下面的无意义的C++代码].有可能接近这个吗?否则将只需要选择"调整"迭代器作为循环内部的第一行我想.
// what I want to do:
std::map<int, std::string> my_map;
// ... populate my_map
for(auto key, auto & value: my_map){
// do something with integer key and string value
}
Run Code Online (Sandbox Code Playgroud)
C++ 11很好,但如果可能,请避免使用.
我得到的最接近的是
// TODO, can this be templated?
struct KeyVal{
int & id;
std::string & info;
template <typename P>
KeyVal(P & p)
: id(p.first)
, info(p.second)
{
}
};
//...
for ( KeyVal kv : my_map ){
std::cout << kv.info;
}
Run Code Online (Sandbox Code Playgroud)
但这意味着为每个地图编写一个适配器类:(
// slightly joke answer/"what could possibly go wrong?"
#define key first
#define value second
Run Code Online (Sandbox Code Playgroud)
Yak*_*ont 17
以下Barry启发的方法是编写一个范围适配器.
没有boost或类似的库支持这样做是一件痛苦的事,但是:
写一个范围模板.它存储2 class iterator秒和具有begin()与end()方法(和其他任何你想要的).
编写一个转换迭代器适配器.它需要一个迭代器,并将其包装起来,以便它的值类型由一些函数对象F转换.
写一个to_kv变换器,取一个std::pair<K, V> cv&并返回一个struct kv_t { K cv& key; V cv& value; }.
将3连接成2进1并调用它as_kv.它需要一系列对,并返回一系列键值.
您最终得到的语法是:
std::map<int, std::string> m;
for (auto kv : as_kv(m)) {
std::cout << kv.key << "->" << kv.value << "\n";
}
Run Code Online (Sandbox Code Playgroud)
这很好.
这是一个极简主义的解决方案,实际上并不创建合法的迭代器,但支持for(:):
template<class Key, class Value>
struct kv_t {
Key&& key;
Value&& value;
};
// not a true iterator, but good enough for for(:)
template<class Key, class Value, class It>
struct kv_adapter {
It it;
void operator++(){ ++it; }
kv_t<Key const, Value> operator*() {
return {it->first, it->second};
}
friend bool operator!=(kv_adapter const& lhs, kv_adapter const& rhs) {
return lhs.it != rhs.it;
}
};
template<class It, class Container>
struct range_trick_t {
Container container;
range_trick_t(Container&&c):
container(std::forward<Container>(c))
{}
It begin() { return {container.begin()}; }
It end() { return {container.end()}; }
};
template<class Map>
auto as_kv( Map&& m ) {
using std::begin;
using iterator = decltype(begin(m)); // no extra (())s
using key_type = decltype((begin(m)->first)); // extra (())s on purpose
using mapped_type = decltype((begin(m)->second)); // extra (())s on purpose
using R=range_trick_t<
kv_adapter<key_type, mapped_type, iterator>,
Map
>;
return R{std::forward<Map>(m)};
}
std::map<int, std::string> m() { return {{0, "Hello"}, {2, "World"}}; }
Run Code Online (Sandbox Code Playgroud)
这是非常小的,但有效.我通常不会鼓励这种半for(:)循环伪迭代器用于循环; 使用真实的迭代器只是一个适度的额外成本,并不会让人们后来感到惊讶.
(现在有临时地图支持.不支持平面C数组......)
范围技巧存储容器(可能是引用),以便将临时容器复制到for(:)循环持续时间内存储的对象中.非临时容器Container类型是Foo&某种类型,因此它不会产生冗余副本.
另一方面,kv_t显然只存储引用.可能有一个奇怪的迭代器返回临时数据来破坏这个kv_t实现,但我不确定如何避免它一般而不牺牲在更常见的情况下的性能.
如果您不喜欢上述kv.部分,我们可以做一些解决方案,但它们并不是那么干净.
template<class Map>
struct for_map_t {
Map&& loop;
template<class F>
void operator->*(F&& f)&&{
for (auto&& x:loop) {
f( decltype(x)(x).first, decltype(x)(x).second );
}
}
};
template<class Map>
for_map_t<Map> map_for( Map&& map ) { return {std::forward<Map>(map)}; }
Run Code Online (Sandbox Code Playgroud)
然后:
map_for(m)->*[&](auto key, auto& value) {
std::cout << key << (value += " ") << '\n';
};
Run Code Online (Sandbox Code Playgroud)
足够近?
关于一流元组(以及因此对)的一些提议可能会给你类似的东西,但我不知道提案的状态.
如果进入C++,你可能会得到的语法如下所示:
for( auto&& [key, value] : container )
Run Code Online (Sandbox Code Playgroud)
关于上述->*可憎的评论:
因此,->*它被用作operator bind来自Haskell(与隐式元组解包)一起使用,我们正在为它提供一个lambda,它获取地图中包含的数据并返回void.(Haskell-esque)返回类型变成了void(无)的映射,我将其视为void.
这项技术有一个问题:你输了break;,continue;哪个很糟糕.
一个不那么hackey Haskell启发的变体会期望lambda返回类似的东西void | std::experimental::expected<break_t|continue_t, T>,如果T没有void返回任何内容,如果T是元组类型返回一个地图,并且如果T是一个地图连接返回的地图类型.根据lambda想要的内容(SFINAE样式检测),它也可以解包或不解包所包含的元组.
但这对于答案来说有点多了; 这个题外话指出,上述编程风格并不是一个完整的死胡同.然而,它在C++中是非常规的.
你可以写一个类模板:
template <class K, class T>
struct MapElem {
K const& key;
T& value;
MapElem(std::pair<K const, T>& pair)
: key(pair.first)
, value(pair.second)
{ }
};
Run Code Online (Sandbox Code Playgroud)
具有能够写出优势key和value但不必指定类型的缺点:
for ( MapElem<int, std::string> kv : my_map ){
std::cout << kv.key << " --> " << kv.value;
}
Run Code Online (Sandbox Code Playgroud)
如果my_map是这样的话,这将无法奏效const.你必须做的事情如下:
template <class K, class T>
struct MapElem {
K const& key;
T& value;
MapElem(std::pair<K const, T>& pair)
: key(pair.first)
, value(pair.second)
{ }
MapElem(const std::pair<K const, std::remove_const_t<T>>& pair)
: key(pair.first)
, value(pair.second)
{ }
};
for ( MapElem<int, const std::string> kv : my_map ){
std::cout << kv.key << " --> " << kv.value;
}
Run Code Online (Sandbox Code Playgroud)
一团糟.现在最好的事情就是习惯于写作.first,.second并希望结构化绑定提案通过,这将允许你真正想要的东西:
for (auto&& [key, value] : my_map) {
std::cout << key << " --> " << value;
}
Run Code Online (Sandbox Code Playgroud)
小智 5
使用现代 c++17,现在可以使用结构化绑定。
#include <map>
#include <string>
#include <iostream>
using namespace std;
int main() {
map<int, string> my_map;
my_map[0] = "hello";
my_map[1] = "world";
for (auto&& [key, value] : my_map) {
cout << key << "," << value << "\n";
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
构建它:
$ clang++ -std=c++17 test.cpp -o program
Run Code Online (Sandbox Code Playgroud)
输出:
$ ./program
0,hello
1,world
Run Code Online (Sandbox Code Playgroud)