简体   繁体   English

如何制作一个 C++ 映射容器,其中键是值的一部分?

[英]How to make a C++ map container where the key is part of the value?

I want to store a bunch of key-value objects, but where the value object itself (and references to it) knows its key.我想存储一堆键值对象,但是值对象本身(和对它的引用)知道它的键。 I also want to efficiently lookup these objects given only the key.我还想有效地查找仅给定键的这些对象。

class SomeObject
{
private:
    //String or integer. int seem cheap enough to duplicate with std::map, but
    //strings seem pretty expensive when there may be thousands of objects in existence.
    //Reference/Pointer to key is fine
    const SomeOtherObject key;
    ...other stuff...
public:
    ...methods, some of which use the key in some way...
};
  • std::map标准::地图
    • Seems to require that the storage is an std::pair, such that the value cant access the key.似乎要求存储是 std::pair,这样值就不能访问密钥。 If the value contains the key, it needs to be duplicated.如果值包含键,则需要复制它。
    • Does not actually enforce that the key inside the value does not get changed in some way实际上并不强制值中的键不会以某种方式改变
  • std::set标准::设置
    • Looks like a really good solution, using a custom compare method to provide uniqueness by key, until you realise it made your entire value const, not just the key field.看起来是一个非常好的解决方案,使用自定义比较方法按键提供唯一性,直到您意识到它使您的整个值成为常量,而不仅仅是键字段。
  • std::vector (or other array/list like solutions) std::vector (或其他类似数组/列表的解决方案)
    • Can use linear search, or if the items are kept sorted binary search.可以使用线性搜索,或者如果项目保持排序,则进行二分搜索。 However I suspect this not not optimal in performance terms, and an extra layer of some kind is needed to really implement the desired behaviour with it.但是,我怀疑这在性能方面并不是最佳的,并且需要某种额外的层来真正实现所需的行为。

C++14 std::set::find non-key searches C++14 std::set::find非键搜索

As mentioned at http://en.cppreference.com/w/cpp/container/set/find C++14 has added two new find APIs:http://en.cppreference.com/w/cpp/container/set/find所述,C++14 添加了两个新的find API:

main.cpp主程序

template< class K > iterator find( const K& x );
template< class K > const_iterator find( const K& x ) const;

which allow you to do:这允许您执行以下操作:

main.cpp主程序

#include <cassert>
#include <set>

class Point {
    public:
        // Note that there is _no_ conversion constructor,
        // everything is done at the template level without
        // intermediate object creation.
        //Point(int x) : x(x) {}
        Point(int x, int y) : x(x), y(y) {}
        int x;
        int y;
};
bool operator<(const Point& c, int x) { return c.x < x; }
bool operator<(int x, const Point& c) { return x < c.x; }
bool operator<(const Point& c, const Point& d) {
    return c.x < d;
}

int main() {
    // std::less<> because of:
    // https://stackoverflow.com/questions/20317413/what-are-transparent-comparators
    std::set<Point, std::less<>> s;
    s.insert(Point(1, -1));
    s.insert(Point(2, -2));
    s.insert(Point(0,  0));
    s.insert(Point(3, -3));
    assert(s.find(0)->y ==  0);
    assert(s.find(1)->y == -1);
    assert(s.find(2)->y == -2);
    assert(s.find(3)->y == -3);
    // Ignore 1234, find 1.
    assert(s.find(Point(1, 1234))->y == -1);
}

Tested on Ubuntu 16.10, g++ 6.2.0, with:在 Ubuntu 16.10、 g++ 6.2.0 上测试,使用:

g++ -std=c++14 -Wall -Wextra -pedantic -o main.out main.cpp
./main.out

Using a custom class instead of less<>使用自定义类而不是less<>

This makes things a bit more explicit and allows you to write multiple comparators per class:这使事情更加明确,并允许您为每个类编写多个比较器:

#include <cassert>
#include <set>

class Point {
    public:
        Point(int x, int y) : x(x), y(y) {}
        int x;
        int y;
};

struct PointCmpY {
    // https://stackoverflow.com/questions/20317413/what-are-transparent-comparators
    typedef std::true_type is_transparent;
    bool operator()(const Point& lhs, int rhs) const {
        return lhs.y < rhs;
    }
    bool operator()(int lhs, const Point& rhs) const {
        return lhs < rhs.y;
    }
    bool operator()(const Point& lhs, const Point& rhs) const {
        return lhs.y < rhs.y;
    }
};

int main() {
    std::set<Point, PointCmpY> s;
    s.insert(Point(1, -1));
    s.insert(Point(2, -2));
    s.insert(Point(0,  0));
    s.insert(Point(3, -3));
    assert(s.find(0)->x == 0);
    assert(s.find(-1)->x == 1);
    assert(s.find(-2)->x == 2);
    assert(s.find(-3)->x == 3);
    assert(s.find(Point(1234, -1))->x == 1);
}

See also也可以看看

I feel your pain.我感觉到你的痛苦。 What makes me mad is that set and map are always implemented using the same data-structure under the hood, which is a tree of values parametrized with a key extractor.让我生气的是setmap总是使用相同的数据结构在引擎盖下实现,这是一个用键提取器参数化的值tree Unfortunately there is no such thing in the standard.不幸的是,标准中没有这样的东西。

If boost is OK, use Boost.MultiIndex to achieve what you need.如果 boost 没问题,请使用Boost.MultiIndex来实现您的需求。 Take a look at Boost.Intrusive too.也看看Boost.Intrusive

C++ provides the mutable keyword that would allow you using the second solution -- a set . C++ 提供了mutable关键字,允许您使用第二种解决方案—— set Declaring your value as mutable in your item class will allow modifying it even if the item is const .在您的项目类中将您的值声明为mutable将允许修改它,即使该项目是const See also: Does the 'mutable' keyword have any purpose other than allowing the variable to be modified by a const function?另请参阅:除了允许 const 函数修改变量之外,“mutable”关键字还有其他用途吗?

Or, even simpler, implement an accessor for your value that const_cast s away the constant-ness of the item.或者,更简单的是,为您的值实现一个访问器, const_cast远离项目的常量性。

... but where the value object itself (and references to it) knows its key ...但是值对象本身(和对它的引用)知道它的键

Map:地图:

The object can't know 'its' key, since a pointer to the same object may be added to several maps, using different keys.对象无法知道“它的”键,因为指向同一个对象的指针可能会被添加到多个映射中,使用不同的键。 The key belongs to a map;密钥属于地图; not to an object.不是对象。

Set:放:

What should happen when the value of this member changes?当这个成员的值发生变化时会发生什么? How would you force a reindexing?您将如何强制重新索引? This is why set enforces constness.这就是 set 强制执行常量的原因。

-- ——

You are trying to index items of a given class based on one of its members, but you don't want to copy this member for indexing purposes, and you don't want to make the object const (I assume that you do want to make the member const).您正在尝试根据其成员之一对给定类的项目进行索引,但您不想复制此成员用于索引目的,并且您不想使对象成为 const(我假设您确实想要使成员为 const)。

I would have built it on top of a Red-Black or AVL tree.我会在红黑树或 AVL 树上构建它。

I'm not familiar enough with Boost.MultiIndex suggested by ybungalobill.我对 ybungalobill 建议的 Boost.MultiIndex 不够熟悉。 I'm not sure whether its instantiated code copies the indexed member, or how it handles values changes for this member.我不确定它的实例化代码是否复制了索引成员,或者它如何处理该成员的值更改。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM