c++ - 带有 std::map 的基于 C++ 范围的 for() 循环
问题描述
我有这些类型:
using key = std::string;
using stop_container = std::unordered_map<key, stop_o>;
using stop_name_const_reference = const std::string &;
using stops_const_reference = const stop_container &;
using timetable_const_reference = const timetable &;
using stop_const_reference = const stop_o&;
class timetable {
public:
stop_container timetable_stops;
trip_container timetable_trips;
};
这些方法:
stops_const_reference stops(timetable_const_reference tt)
{
return tt.timetable_stops;
}
stop_name_const_reference stop_name(stop_const_reference st)
{
return st.first;
}
然后我的问题是循环范围:
for (auto&& stop : stops(tt))
{
auto&& stop_name_ = stop_name(stop);
}
我可以这样重写它以便更好地理解:
for (auto&& stop : const std::unordered_map<key, stop_o> &)
{
auto&& stop_name_ = stop_name(stop);
}
对于方法 stop_name 我需要const stop_o
作为参数,但从循环中我得到const std::pair<const key, stop_o>
. 我知道这就是地图在那里工作的方式,但是我能否以某种方式实现仅具有来自 pair 的值而根本不更改 for 循环?
解决方案
for (auto const& [key, stop] : stops(tt))
{
auto&& stop_name_ = stop_name(stop);
}
这使用了一种称为“结构化绑定”的技术来将该对转换为对该对元素的两个引用。
在结构化绑定之前,您可以完成
for (auto const& elem : stops(tt))
{
const auto& stop = elem.second;
auto&& stop_name_ = stop_name(stop);
}
如果要保持 for 循环的主体和初始语句不变,则必须更改stops
.
如果stop_o
可以免费复制:
std::vector<stop_o> stops(timetable_const_reference tt);
写起来不应该很昂贵。
如果没有,你必须花哨。
template<class T>
struct pseudo_pointer {
T t;
T* operator->() { return std::addressof(t); }
};
template<class F, class It>
struct mapping_input_iterator {
using iterator_category = std::input_iterator_tag;
using reference = std::invoke_result_t<F, typename std::iterator_traits<It>::reference>;
using value_type = std::decay_t<reference>;
using difference_type = typename std::iterator_traits<It>::difference_type;
using pointer = pseudo_pointer<reference>;
F f;
It it;
mapping_input_iterator& operator++() {
++it; return *this;
}
mapping_input_iterator operator++(int) {
auto retval = *this; ++(*this); return retval;
}
bool operator==(mapping_input_iterator const& other) const {
return it == other.it;
}
bool operator!=(mapping_input_iterator const& other) const {
return !(*this == other);
}
reference operator*() const {return f(*it);}
pointer operator->() const {return {f(*it)};}
};
template<class F, class It>
mapping_input_iterator(F, It)->mapping_input_iterator<F, It>;
template<class It, class Sentinal=It>
struct range {
It b;
Sentinal e;
It begin() const { return b; }
Sentinal end() const { return e; }
};
template<class It>
range(It, It)->range<It, It>;
然后在上述工作之上编写一些实用函数:
template<class C>
auto get_keys( C const& c ) {
auto key_getter = [](auto const& pair)->decltype(auto) { return pair.first; };
using std::begin; using std::end;
mapping_input_iterator b{ key_getter, begin(c) };
mapping_input_iterator e{ key_getter, end(c) };
return range{std::move(b),std::move(e)};
}
template<class C>
auto get_values( C const& c ) {
auto value_getter = [](auto const& pair)->decltype(auto) { return pair.second; };
using std::begin; using std::end;
mapping_input_iterator b{ value_getter, begin(c) };
mapping_input_iterator e{ value_getter, end(c) };
return range{std::move(b),std::move(e)};
}
现在我们可以做这个魔术:
auto stops(timetable_const_reference tt)
{
return get_values(tt.timetable_stops);
}
如果您害怕上面的代码,那么(在最近的 C++ 标准中)有类似的实用程序,这是合理boost
的。std::range
请注意,当一个稍微简单的版本可以工作时,我以一些小方式“正确”地做了一堆东西。
要执行for(:)
循环,您不需要实际的迭代器,您只需要一个可以说服for(:)
循环生成代码的迭代器。我实际上写了一个真正的迭代器包装器。
还
template<class It>
range(It, It)->range<It, It>;
故意不允许您将哨兵类型推断为与迭代器不同;这很容易意外出错,如果你想要一个哨兵,你必须明确地传递它。尽管如此,我range
还是支持单独的结束前哨,因为我写的不简单。
OTOH,我可能应该使值/键获取器不是匿名的本地 lambda,因此人们可以命名get_keys
返回的范围类型而无需执行 decltype。那好吧。
推荐阅读
- jira - Reportportal 到 JIRA 集成不起作用
- javascript - 基本的 javascript-insertAdjacentHTML
- php - 这个类继承的例子有什么问题?
- javascript - 使用拖动道具时,如何让 React Framer-motion 触发手机的 onClick 事件?
- react-native - setDefaultOptions VS。静态选项 VS。setRoot 中的选项
- html - Fancybox 导致分屏布局滚动位置重置为顶部
- oracle - oracle apex 错误处理功能无法正常工作
- sql - MS Access - 通过连接一些列来查找重复值
- python - 哪些字符会为 excel 单元格(xlsx 文件格式)调用 alt+enter?(wrap_text 不起作用)
- c++ - 获取调用函数调用的 C++ 程序中的所有行号