【问题标题】:Using a key that takes into account rollover or wrap of the key value in std::map使用考虑翻转或包装 std::map 中键值的键
【发布时间】:2019-04-09 00:04:30
【问题描述】:

是否有可能为std::map 提供一个考虑翻转或换行的密钥?如果是这样,您将如何实现

假设键基于具有最大值 N 的计数器,是否可以有一个

 N-2
 N-1
 N
 1
 2
 3
 ...

另外,如果可以跳过键值,是否可以这样做?例如,跳过 N 以便地图具有以下值:

 N-2
 N-1
 1
 2
 3
 ...

【问题讨论】:

    标签: c++ c++11 dictionary std stdmap


    【解决方案1】:

    是的。

    < 用于std::tuplebool,您可以执行类似的操作

    #include <iostream>
    #include <string>
    #include <map>
    #include <functional>
    
    template <typename T>
    struct WrappedLess
    {
        T wrap_point;
        bool operator()(const T & lhs, const T & rhs) const
        {
            return std::make_tuple(lhs < wrap_point, std::ref(lhs)) < std::make_tuple(rhs < wrap_point, std::ref(rhs));
        }
    };
    
    constexpr int N = 100;
    
    int main() 
    {
        std::map<int, std::string, WrappedLess<int>> wrapped_values({N-2}); // initally empty
    
        std::map<int, std::string, WrappedLess<int>> more_wrapped_values({
            {N-10, "last"}, 
            {N-2, "first"}, 
            {N-1, "second"}, 
            {1, "third"}, 
            {5, "fourth"}, 
        }, {N-2}); // initialised with some values
    
        for (auto & pair : more_wrapped_values)
            std::cout << pair.first << pair.second << std::endl;
    }
    

    See it live on coliru

    【讨论】:

    • 是否可以将此扩展为具有 对的类的键?当我尝试它获取该类上的
    • 所以我一直在用你的方法做一些测试。本质上,这是为地图实现自定义比较,而不是默认的较少。我实施了您提出的比较,但它实际上不起作用。使用 MAX 25,当插入到地图中时,翻转附近的结果如下,对于键为 21、22、23、24、25、1、2 25 1 2 21 22 23 24 的元素点”为 MAX + 1 更正键 25 的位置,但键 1 无法正常工作。
    【解决方案2】:

    这是一个如何实现的示例。这个想法是引入一个自定义的Key 模板,该模板可以在编译时使用所需的最大值进行参数化。比较和基本算术运算通过boost operator library 提供,因此Key 实例具有相同的最大值。值可以比较和相加。

    #include <boost/operators.hpp>
    
    template <int max>
    struct Key : private boost::totally_ordered<Key<max>, boost::addable<Key<max>>> {
       int value;
    
       Key(int init = 0) : value(init) { maybeRollOver(); }
    
       Key& operator+=(const Key& rhs) { value += rhs.value; maybeRollOver(); return *this; }
       const Key& operator+() const { return *this; }
    
       void maybeRollOver() { if (value > max) value = value % max - 1; }
    };
    

    此模板需要两个运算符重载来生成所有比较运算符(参见boost docs):

    template <int max>
    bool operator==(const Key<max>& lhs, const Key<max>& rhs)
    {
       return lhs.value == rhs.value;
    }
    
    template <int max>
    bool operator<(const Key<max>& lhs, const Key<max>& rhs)
    {
       return lhs.value < rhs.value;
    }
    

    这里是如何使用它。

    std::map<Key<5>, std::string> testMap;
    
    testMap[0] = "Hello";
    testMap[1] = "world";
    testMap[6] = "Bye"; // "rolled over to 0, "Hello" is replaced by "Bye";
    
    for (const auto& [key, value] : testMap)
       std::cout << key.value << ": " << value << "\n";
    

    【讨论】:

    • 谢谢!不幸的是,我们没有使用 boost :-(
    • @Louise boost 基类除了插入样板代码之外什么都不做,即基于现有代码的两个Key 实例的operator+operator !=。您可以轻松删除基类并自己实现所需的内容。
    【解决方案3】:

    这是我能够开始工作的解决方案。

    键实际上是&lt;unsigned int, unsigned int&gt;,相对于翻转的顺序只需要考虑键的第一个值。 此外,ID 基于 1,因此值为 1, 2, ..., N, 1, 2, ... 最后,请注意 MAX ID 足够大,因此我们永远不会有多个相同的键试图同时存在于映射中。也就是说,当我们到达 id N 时,最初的 1、2、3... 键早已不复存在。

    class Mykey(
    public:
       unsigned int Id1;
       unsigned int Id2;
    
    MyKey(unsigned int k1, unsigned in k2)
    : Id1(k1), Id2(k2) {}
    
    bool operator<(const MyKey &rhs) const
    {
        if (Id1 == rhs.Id1)
            return Id2 < rhs.Id2;
        else if ( (rhs.Id1 > Id1) && (rhs.Id1 - Id1 > MAX_ID/2) )
            return false;
        else if ( (Id1 > rhs.Id1) && (Id1 - rhs.Id1 > MAX_ID/2) )
            return true;
        else
            return Id1 < rhs.Id1;
    }
    

    假设 MAX_ID 为 25,第一个 else if 考虑 rhs.Id1 为 25 且 Id1 为 1 的情况。第二个 else 考虑相反的情况,即 Id1=25 和 rhs.Id1=1。 MAX_ID/2 正在检查这两个值是否“相距甚远”,表示 ID 上的回绕。

    【讨论】:

    • std::map 无法表达您想要的行为。比较需要提供一个排序,使得a &lt; bb &lt; c 暗示a &lt; c。在这种情况下,如果您可以拥有MyKey a = { MAX_ID - 4, 0 }, b = { MAX_ID/2 - 2, 0 }, c = { 0, 0 },我们将拥有a &lt; bb &lt; cc &lt; a
    • 使用与std::map 的比较具有未定义的行为。最终可能会导致寻找特定密钥导致程序崩溃或损坏不相关数据
    • 嗯...好点!所以这不是一个通用的解决方案。但是,在特定的代码集中,我不会像你所说的那样使用 a、b、c 的情况。地图一次永远不会有超过 10-20 个条目。 MAX_ID 是 255。因此,条目相距很远 a = { MAX_ID - 4, 0 }, b = { MAX_ID/2 - 2, 0 } 是不合理的。
    猜你喜欢
    • 2017-09-28
    • 2011-10-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-06
    • 2011-06-23
    • 2014-08-06
    相关资源
    最近更新 更多