【问题标题】:how to match nocase c-string in boost multi_index_container by hash如何通过哈希匹配boost multi_index_container中的nocase c-string
【发布时间】:2020-07-28 15:10:10
【问题描述】:

查看下面的代码,我为 nocase-c-string key(const char*) 重载了 hash 函数和 string-equal 函数,但结果不是我所期望的:主函数将打印找不到,如何制作匹配吗?

struct hash_c_string
{
    std::size_t operator()(const char *ctx) const
    {
        return MurmurHash2(ctx, strlen(ctx),static_cast<size_t>(0xc70f6907UL));
    }
};
typedef struct  {
    const char * name;
}PortMapConfig;
struct by_name{};
struct CompareEqual
{   
    inline bool operator()(const char* left, const char* right) const
    {
        return strcasecmp(left, right)==0;
    }
};
using namespace boost::multi_index;
 
typedef
boost::multi_index_container<
        PortMapConfig,
        indexed_by<
      hashed_unique<tag<by_name>, member<PortMapConfig, const char *, &PortMapConfig::name>, hash_c_string, CompareEqual>

        >
> StuContainer;
int main() {
    StuContainer con;
    PortMapConfig st1 = {"Uplink0"};
    con.insert(st1);
    auto &indexofName = con.get<by_name>();
    const char * s = "uplink0";
    auto itr = indexofName.find(s);
    if(itr != indexofName.end()){
        std::cout << "name:"<<itr->name << std::endl;
    }
    else
        std::cout << "not found!!!"<< std::endl;
 
    return 0;
}

【问题讨论】:

  • 你的哈希函数和你的等价函数不匹配。等价函数忽略大小写,散列没有,所以两个等价的东西有不同的散列。行为未定义。

标签: c++ boost multi-index


【解决方案1】:

您正在尝试通过派生属性(即大小写折叠名称)进行散列。

即使做得好¹,这也会很昂贵。这里的所有信号都表明您可能以性能的名义进行这些操作。如果您需要不区分大小写的查找,我建议您具体化一个要索引的键,这样您就可以对它们进行自然散列和相等性。

¹重要的是,正如评论者已经指出的那样,您的解决方案有 Undefined Behaviour,因为哈希/相等函数不同意

您甚至可以预先折叠查找键以避免重复操作。

在这个例子中,我强烈建议使用字符串视图,考虑到根本不进行散列(您是否使用负载因子对其进行了分析?)。

使您的示例工作,并进行了一些清理:

Live On Coliru

#include <boost/locale.hpp>
#include <boost/locale/conversion.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index_container.hpp>
#include <iostream>
namespace bmi = boost::multi_index;

static auto inline fold_case(std::string_view sv) {
    return boost::locale::fold_case(sv.data());
}

struct PortMapConfig {
    /*explicit*/ PortMapConfig(std::string_view name): name(name) {}

    std::string_view name;
    std::string name_key = fold_case(name);

    struct Hash {
        std::size_t operator()(std::string_view sv) const {
            return boost::hash_value(sv); // TODO(sehe): murmur instead
        }
    };

    using Equal = std::equal_to<std::string_view>;
};

using StuContainer = bmi::multi_index_container<
    PortMapConfig,
    bmi::indexed_by<bmi::hashed_unique<
        bmi::tag<struct by_name>,
        bmi::member<PortMapConfig, std::string, &PortMapConfig::name_key>,
        PortMapConfig::Hash,
        PortMapConfig::Equal>
    >>;

int main() {
    std::locale::global(boost::locale::generator()("en_US.UTF-8"));
    StuContainer con { {"Uplink0"}, {"Uplink1"} };

    if (auto itr = con.find(fold_case("uplink1")); itr != con.end()) {
        std::cout << "name:" << itr->name << " (" << itr->name_key << ")\n";
    }
}

打印

name:Uplink1 (uplink1)

【讨论】:

    猜你喜欢
    • 2019-10-10
    • 2020-12-08
    • 2013-08-20
    • 2014-05-19
    • 2013-04-07
    • 1970-01-01
    • 2021-04-23
    • 1970-01-01
    相关资源
    最近更新 更多