如何从集合初始化地图
How to initialize a map from a set
我想使用 std::set
(或 std::vector
)的元素设置 std::map
的键。
类似下面的内容...
std::set<int> keys = { 3,4,6 };
std::map<int,string> results(keys); // syntax error
这可以在不显式迭代集合的情况下完成吗?
你不能。 map
不是 set
。它们是根本不同的容器,即使底层结构相似。
也就是说,一切皆有可能。如果范围的元素已经排序,std::map
的范围构造函数是线性时间,set
为我们保证。所以你需要做的就是对每个元素应用一个转换器来产生一个新的范围。最简单的方法是只使用 boost::make_transform_iterator
之类的东西(或自己滚动):
template <class K, class F
class V = decltype(std::declval<F&>()(std::declval<K const&>()))::second_type>
std::map<K, V> as_map(std::set<K> const& s, F&& f) {
return std::map<K,V>(
boost::make_transform_iterator(s.begin(), f),
boost::make_transform_iterator(s.end(), f));
}
std::map<int,string> results =
as_map(keys, [](int i){
return std::make_pair(i, std::string{});
});
如果你总是想要默认初始化,可以减少到:
template <class V, class K>
std::map<K, V> as_map_default(std::set<K> const& s) {
auto f = [](K const& k) { return std::make_pair(k, V{}); }
return std::map<K,V>(
boost::make_transform_iterator(s.begin(), f),
boost::make_transform_iterator(s.end(), f));
}
std::map<int,string> results = as_map_default<string>(keys);
Can this be done without explicitly iterating over the set?
没有。如果不迭代它,就无法知道集合中的键。您可以编写函数使其 看起来 就好像存在隐式转换一样,但这些函数最终必须迭代源集合。
简单的方法如下:
#include <set>
#include <map>
#include <string>
auto build_map(const std::set<int>& source) -> std::map<int,std::string>
{
std::map<int,std::string> results;
for (auto const& i : source) {
results[i];
}
return results;
}
int main()
{
std::set<int> keys = { 3,4,6 };
auto results = build_map(keys);
}
当然,如果这样可以提高可读性,我们可以进行模板化:
#include <set>
#include <vector>
#include <unordered_set>
#include <map>
#include <string>
#include <utility>
template<class MappedType, class SourceContainer>
auto build_map(SourceContainer&& source)
{
using source_type = std::decay_t<SourceContainer>;
using key_type = typename source_type::value_type;
std::map<key_type , MappedType> results;
for (auto const& i : source) {
results[i];
}
return results;
}
int main()
{
std::set<int> keys = { 3,4,6 };
auto results = build_map<std::string>(keys);
// also
results = build_map<std::string>(std::vector<int>{3, 4, 6});
results = build_map<std::string>(std::unordered_set<int>{3, 4, 6});
}
我想使用 std::set
(或 std::vector
)的元素设置 std::map
的键。
类似下面的内容...
std::set<int> keys = { 3,4,6 };
std::map<int,string> results(keys); // syntax error
这可以在不显式迭代集合的情况下完成吗?
你不能。 map
不是 set
。它们是根本不同的容器,即使底层结构相似。
也就是说,一切皆有可能。如果范围的元素已经排序,std::map
的范围构造函数是线性时间,set
为我们保证。所以你需要做的就是对每个元素应用一个转换器来产生一个新的范围。最简单的方法是只使用 boost::make_transform_iterator
之类的东西(或自己滚动):
template <class K, class F
class V = decltype(std::declval<F&>()(std::declval<K const&>()))::second_type>
std::map<K, V> as_map(std::set<K> const& s, F&& f) {
return std::map<K,V>(
boost::make_transform_iterator(s.begin(), f),
boost::make_transform_iterator(s.end(), f));
}
std::map<int,string> results =
as_map(keys, [](int i){
return std::make_pair(i, std::string{});
});
如果你总是想要默认初始化,可以减少到:
template <class V, class K>
std::map<K, V> as_map_default(std::set<K> const& s) {
auto f = [](K const& k) { return std::make_pair(k, V{}); }
return std::map<K,V>(
boost::make_transform_iterator(s.begin(), f),
boost::make_transform_iterator(s.end(), f));
}
std::map<int,string> results = as_map_default<string>(keys);
Can this be done without explicitly iterating over the set?
没有。如果不迭代它,就无法知道集合中的键。您可以编写函数使其 看起来 就好像存在隐式转换一样,但这些函数最终必须迭代源集合。
简单的方法如下:
#include <set>
#include <map>
#include <string>
auto build_map(const std::set<int>& source) -> std::map<int,std::string>
{
std::map<int,std::string> results;
for (auto const& i : source) {
results[i];
}
return results;
}
int main()
{
std::set<int> keys = { 3,4,6 };
auto results = build_map(keys);
}
当然,如果这样可以提高可读性,我们可以进行模板化:
#include <set>
#include <vector>
#include <unordered_set>
#include <map>
#include <string>
#include <utility>
template<class MappedType, class SourceContainer>
auto build_map(SourceContainer&& source)
{
using source_type = std::decay_t<SourceContainer>;
using key_type = typename source_type::value_type;
std::map<key_type , MappedType> results;
for (auto const& i : source) {
results[i];
}
return results;
}
int main()
{
std::set<int> keys = { 3,4,6 };
auto results = build_map<std::string>(keys);
// also
results = build_map<std::string>(std::vector<int>{3, 4, 6});
results = build_map<std::string>(std::unordered_set<int>{3, 4, 6});
}