C++ 试图(稍微)推广C++;模板。关联容器键:值反转
下面函数模板的目标是获取任何C++ 试图(稍微)推广C++;模板。关联容器键:值反转,c++,templates,refactoring,C++,Templates,Refactoring,下面函数模板的目标是获取任何无序映射,并生成一个新的无序映射,其中键类型和映射类型倒置。 下面的函数适用于std::无序映射。我希望它能在std::unordered_-map和任何stl hashmap模拟中起到额外的作用 我想维护的另一个好处是,在调用函数时,如果需要默认行为,autoinversion=InvertHashMap(someIntStringMap)不使用模板参数。但是,如果我提供了有效的初始模板参数,我可以覆盖默认的哈希器,例如用于构建反转映射的哈希器 我很难使容器成为泛型
无序映射
,并生成一个新的无序映射
,其中键类型
和映射类型
倒置。
下面的函数适用于std::无序映射
。我希望它能在std::unordered_-map
和任何stl hashmap模拟中起到额外的作用
我想维护的另一个好处是,在调用函数时,如果需要默认行为,autoinversion=InvertHashMap(someIntStringMap)
不使用模板参数。但是,如果我提供了有效的初始模板参数,我可以覆盖默认的哈希器,例如用于构建反转映射的哈希器
我很难使容器成为泛型的,同时仍然基于容器的5个模板参数提供默认的可启用模板参数。一旦我将容器本身设置为模板参数,重载解析就会失败,编译也会失败
我很乐意将关联容器作为唯一的模板参数,但是影响输出容器的模板参数的能力就丧失了,至少在我的非灵活示例中,它们可以显式模板化
#include <unordered_map>
#include <utility>
#include <functional>
#include <memory>
template <typename InKeyType,
typename InValueType,
typename InHasher,
typename InEq,
typename InAlloc,
typename OutHash = std::hash<InValueType>,
typename OutEq = std::equal_to<InValueType>,
typename OutAlloc=std::allocator<std::pair<constInValueType,InKeyType>>>
std::unordered_map<InValueType, InKeyType, OutHash, OutEq, OutAlloc>
InvertMap(const std::unordered_map<InKeyType, InValueType, InHasher, InEq, InAlloc>& source)
{
std::unordered_map<InValueType, InKeyType, OutHash, OutEq, OutAlloc> outMap;
for (const auto& sourceKVPair : source)
outMap[std::get<1>(sourceKVPair)] = std::get<0>(sourceKVPair);
return outMap;
}
//in a .cpp
unordered_map<int,string> um;
auto newUM = InvertHashMap(um); //works well; newUM::key_type is string
双重编辑:在我急于提供一个示例的过程中,我选择了
std::map
作为另一个paramater示例,我意识到它既没有哈希器,也没有五个模板参数。因此,我的问题的基础仍然是尝试使此函数多样化,但是特别针对具有五个模板参数且行为兼容的参数…。我已经编辑了我的帖子,以减轻这种疏忽。我觉得这可能更好地以类似的方式实施。换句话说,设计反转函数,将一系列迭代器放入输入容器,将迭代器放入输出容器。它将更易于实现,并为用户提供更大的灵活性。此外,只要输入和输出迭代器满足某些条件(可能是由用户强加的),它将在某种程度上独立于容器类型。下面是一个示例,它可能不是您想要的,但您可能可以修改它以满足您的需要:
#include <algorithm>
#include <iostream>
#include <map>
#include <string>
#include <unordered_map>
namespace
{
template <class InputIt, class OutputIt>
void inverse_map(InputIt start, InputIt stop, OutputIt d_first)
{
while(start != stop)
{
*d_first = {start->second, start->first} ;
++d_first ;
++start ;
}
}
} // anonymous namespace
int main()
{
std::map<int, std::string> map_1 {{1, "foo"}, {2, "bar"}, {3, "foo"}} ;
std::unordered_map<std::string, int> map_2 ;
//
// Or, you can use:
//
// std::unordered_map<std::string, int, MyCustomHasher> map_2 ;
//
inverse_map(map_1.begin(), map_1.end(), std::inserter(map_2, map_2.end())) ;
for(const auto& [key, value]: map_2) // requires C++17
std::cout << key << ": " << value << "\n" ;
return 0;
}
在线试用。你能使用
std::map::key_type
和std::map::mapped_type
(对于std::unordered_map
)吗?@crayzeewulf如果我试过,但不幸的是,如果容器的模板参数不是模板定义的一部分,用户无法覆盖它们。如果默认的分配器、哈希器和相等函子都是必需的,那么容器是唯一的模板参数,使用作用域穿透来获取相关类型就可以了。我只想覆盖出站模板参数,但它们是基于输入的。我喜欢这个想法,但没有我脑子里想的那么多。auto-IntStringMap=InvertMap(StringIntMap)的优雅代码>不可发布。无需预先声明out容器,也无需对任何类型的迭代器进行参数化。事实证明,模板不是魔术实体,它使C++像动态语言,而牺牲了性能。我想我可以为我知道我想要的特定容器(比如我的示例)创建专门的版本,并为所有其他情况创建迭代器版本。当然,对于手头的特定容器和类型,总是可以选择手动编写流程。@schulmaster我明白你的意思。和你一样,我也尝试过实现基于容器的算法,因为它们显然很简单。然而,一次又一次地推广它们,导致了太多的复杂情况。在大多数情况下,我最终使用了基于范围的算法(比如标准库中的算法)。这是一些讨论。C++很快就会简化事物。在许多其他方面,使用基于迭代器的方法的一个优点是它将算法与容器解耦。因此,这些算法适用于现有/预期的容器以及未来/非预期的容器。如果您最终自行设计了一个支持迭代器的全新map类,那么基于迭代器的实现很可能在不做任何修改的情况下工作。然而,基于容器的方法很可能需要重写/重构。
#include <algorithm>
#include <iostream>
#include <map>
#include <string>
#include <unordered_map>
namespace
{
template <class InputIt, class OutputIt>
void inverse_map(InputIt start, InputIt stop, OutputIt d_first)
{
while(start != stop)
{
*d_first = {start->second, start->first} ;
++d_first ;
++start ;
}
}
} // anonymous namespace
int main()
{
std::map<int, std::string> map_1 {{1, "foo"}, {2, "bar"}, {3, "foo"}} ;
std::unordered_map<std::string, int> map_2 ;
//
// Or, you can use:
//
// std::unordered_map<std::string, int, MyCustomHasher> map_2 ;
//
inverse_map(map_1.begin(), map_1.end(), std::inserter(map_2, map_2.end())) ;
for(const auto& [key, value]: map_2) // requires C++17
std::cout << key << ": " << value << "\n" ;
return 0;
}
bar: 2
foo: 1