std ::类似map的类型,没有key / hash on key

时间:2014-04-13 15:47:52

标签: c++ c++11 stl containers stdmap

我遇到了一个小问题,并想知道许多解决方案中哪一个是最好的/适当的。

我有一个std::map,每个都有一个键和值的自定义类(所以交换它们不会修复任何东西)。

struct FooStruct
{
    bool testOne;
    bool testTwo;
};

struct BarStruct
{
    // some data
};

const std::map<FooStruct, BarStruct>
{
    { { true, false }, BarStruct(someValues) },
    { { false, true }, BarStruct(someOtherValues) }
};

现在,FooStruct无法以任何明智的方式进行“比较”BarStruct。使用unordered_map也不是一个帮助,因为这需要散列函数,我当然实现了很多方法,但我怀疑这是获取真正未分类映射的最简单方法。

我也不关心表现,因为地图中只有4个元素。可能还有几千次。

解决评论:这是一个抽象的例子。问题是,如果有一些测试,可以很容易地比较结构中布尔测试结果的集合,但随着排列的数量与n的增长非常快,我正在寻找可扩展的解决方案。

也许整体上有std::map种类型的替代品,例如: std::vector的{​​{1}},但也有其他缺点。

3 个答案:

答案 0 :(得分:2)

如果比较是“明智的”并不重要,重要的是它实现了严格的弱排序。这很容易实现。

#include <tuple>

bool comp(const FooStruct& lhs, const FooStruct& rhs)
{
  return std::tie(lhs.testOne, lhs.testTwo) < 
         std::tie(rhs.testOne, rhs.testTwo);
}

对于unordered_map,它是一个哈希表,因此您需要提供哈希函数和相等比较。没有办法解决这个问题。

答案 1 :(得分:2)

如果您的结构FooStruct有许多测试结果,那么您有不同的结构,并且测试次数会有所不同,而不是您没有可扩展的解决方案。

使用bitsetref)编写可扩展版本。然后,您可以使用bitset::to_ulongref)进行比较(假设您的测试结果少于64个)。

struct FooStruct
{
    std::bitset<5> result; // can hold 5 results
    friend bool operator<(const FooStruct& a, const FooStruct& b) {
        return a.result.to_ulong() < b.result.to_ulong();
    }
};

否则您必须手动汇总。例如:

struct FooStruct
{
    bool testOne;
    bool testTwo;
    bool testThree;
    unsigned long key() const {
        return testOne + (testTwo << 1) + (testThree << 2);
    }
    friend bool operator<(const FooStruct& a, const FooStruct& b) {
        return a.key() < b.key();
    }
};

答案 2 :(得分:0)

另一个&#34;可扩展&#34;解决方案:

using FooStruct = std::vector<bool>;
std::map<FooStruct, BarStruct> foobarite
{
    { { true, false }, {} },
    { { false, true }, {} },
};

并且,如果你想在FooStruct中保留命名属性,还有另一个:

#include <unordered_map>
#include <functional>
#include <algorithm>

template <class T>
inline void hash_combine(std::size_t & seed, const T & v)
{
  std::hash<T> hasher;
  seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}

template <typename Struct>
struct hash {
    inline std::size_t operator()(const Struct& obj ) const
    {
        const unsigned char* p = reinterpret_cast<const unsigned char*>( &obj );
        std::size_t seed = std::hash<unsigned char>{}(p[0]);
        for (unsigned int i = 1; i < sizeof(obj); ++i) {
            hash_combine(seed, p[i]);
        }
        return seed;
    }
};

template <typename Struct>
struct equal {
    bool operator()(const Struct& a, const Struct& b) const
    {
        const unsigned char* pa = reinterpret_cast<const unsigned char*>( &a );
        const unsigned char* pb = reinterpret_cast<const unsigned char*>( &b );
        return std::equal(pa, pa+sizeof(Struct), pb);
    }
};

struct FooStruct {
    bool testOne;
    bool testTwo;
};

std::unordered_map<FooStruct, BarStruct, hash<FooStruct>, equal<FooStruct>> foobarite
{
    { { true, false }, {} },
    { { false, true }, {} }
};