[英]How do I create a templated function can that operate on any std::map that has a string key?
我想编写一个不区分大小写的模板化版本的 find 函数,它执行如下操作:
#define TKEYSTR char*
#define TVALUE int
typedef std::map<TKEYSTR, TVALUE> tStrMap;
auto findKeyIC(const tStrMap& map, const TKEYSTR k)
{
using TIterator = tStrMap::const_iterator;
TIterator it;
for (it = map.cbegin(); it != map.cend(); map++)
{
if (boost::iequals(map.first, k))
return it;
}
return it;
}
这里的目标是搜索一个包含键值对的容器,其中键是一个字符串(char*、wchar_t*、std::string 或 std::wstring),如果不匹配则返回匹配的条目或 cend。
如何将其定义为处理所有不同情况的模板? 我可以用一个模板来做吗? 我可以以某种方式使用 4 个显式模板吗?
我试过:
template<typename _Tvalue>
auto findKeyIC(std::map<char*, _Tvalue>& map, const char* k)
...
template<typename _Tvalue>
auto findKeyIC(std::map<wchar_t*, _Tvalue>& map, const wchar_t* k)
...
但这是不允许的,因为我两次声明相同的模板。
最后,是否有可能使这项工作适用于更广泛的容器,而不仅仅是映射? 即任何具有某种形式的字符串键的容器?
谢谢
朱尔斯
您可以彻底解决问题并创建一个自定义比较函子来进行不区分大小写的比较。 这将使查找更快、更易于使用(因为您不必依赖外部函数)。 它可能看起来像这样。
template <typename String>
struct ILess {
bool operator()(const String &lhs, const String &rhs) const noexcept {
return boost::ilexicographical_compare(lhs, rhs);
}
};
template <typename String>
struct IEqualTo {
bool operator()(const String &lhs, const String &rhs) const noexcept {
return boost::iequals(lhs, rhs);
}
};
template <typename String>
struct IHash {
size_t operator()(const String &str) const noexcept {
size_t hash = 0;
for (const auto ch : str) {
boost::hash_combine(hash, std::toupper(ch));
}
return hash;
}
};
template <typename Key, typename Value>
using IMap = std::map<Key, Value, ILess<Key>>;
template <typename Key, typename Value>
using IUnorderedMap = std::unordered_map<Key, Value, IHash<Key>, IEqualTo<Key>>;
当然,如果您需要在同一个容器中进行区分大小写和不区分大小写的查找,那么这将不起作用。
您可以将函数定义为与地图模板并行的模板:
template <typename Key, typename Value>
auto findKey(std::map<Key, ValueType> const& data, Key const& key);
对于任意容器,您可以添加模板模板参数:
template <typename Key, typename Value, template<typename, typename> class Container>
auto findKey(Container<Key, Value> const& data, Key const& key);
因此,您可以在有序和无序地图以及您自己的自定义地图上使用该函数。
照原样,您甚至不仅限于字符串键,还可以使用任何类型。
根据@Aconcagua 的回答,我编写了以下代码来解决问题。 我必须专门处理 char* 和 wchar* 函数,因为 boost iequal 仅适用于字符串。
template <typename Key, typename Value>
auto findKeyIC(std::map<Key, Value> const& map, Key const& key)
{
using TIterator = std::map<Key, Value>::const_iterator;
TIterator it;
for (it = map.cbegin(); it != map.cend(); it++)
{
if (boost::iequals(it->first, key))
return it;
}
return it;
}
template <typename Value>
auto findKeyIC(std::map<char*, Value> const& map, char* const& key)
{
using TIterator = std::map<char*, Value>::const_iterator;
TIterator it;
for (it = map.cbegin(); it != map.cend(); it++)
{
if (_stricmp(it->first, key) == 0)
return it;
}
return it;
}
template <typename Value>
auto findKeyIC(std::map<wchar_t*, Value> const& map, wchar_t* const& key)
{
using TIterator = std::map<wchar_t*, Value>::const_iterator;
TIterator it;
for (it = map.cbegin(); it != map.cend(); it++)
{
if (_wcsicmp(it->first, key) == 0)
return it;
}
return it;
}
更新:上面的代码不太有效,根据建议,下面的代码更清晰,更通用。 来自 Aconcagua 的更多更正和建议最终使所有版本都能正常工作,我在让它接受字符串文字时遇到了很多麻烦,但现在也很好。
template<typename Key>
bool IsKeyEqualI(const Key & str1, const Key & str2)
{
return boost::iequals(str1, str2);
}
bool IsKeyEqualI(const char *const& str1, const char *const& str2)
{
return (_stricmp(str1, str2) == 0);
}
bool IsKeyEqualI(const wchar_t *const& str1, const wchar_t *const& str2)
{
return (_wcsicmp(str1, str2) == 0);
}
template <typename Key, typename Value, typename Reference, template<typename ...> class Container>
auto findKeyIC(Container<Key, Value> & container, Reference const & key)
{
auto it = container.begin();
for (; it != container.end(); ++it)
{
if (IsKeyEqualI(it->first, key))
return it;
}
return it;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.