STL表示暗示交叉引用的数据结构的方式

时间:2016-05-13 18:44:18

标签: c++ oop stl c++17 incomplete-type

我经常遇到以下情况。 (不失一般性:我在以下示例中使用了两个容器的最简单的可能情况,但在几何算法的实现中,需要大量的描述互连图形数据结构。)

我有两个数据类型AB的大量值,它们相互引用(通常不是一对一),比如说,首先是通过(本机)指针或引用。它们都放置在容器using CA = std::container1< A >;using CB = std::container2< B >;中。某些函数的结果是一对CACB个实例。拥有CA实例的元素我想删除CB实例中的引用元素,反之亦然。

struct A;
struct B;

using CA = std::container1< A >;
using CB = std::container2< B >;

我想将AB定义如下:

struct A
{
    int payload;
    typename CB::iterator pb; // hard error here in general case of choosing `std:container2`
};

struct B
{
    double payload;
    typename CA::iterator pa;
};

// ...
PA a;
PB b;
// ...
assert(!a.empty());
assert(a.begin()->pb != b.end()); // and pb is not default-constructed
b.erase(a.begin()->pb);

Live example.

但目前我无法在一般情况下声明typename CB::iterator pb;,只是B /*const*/ * pb;B /*const*/ & pb;,因为B类型是{CB的一部分1}}声明,在将容器typedef iterator的成员CB用于聚合A的定义时不完整。

有一个不完整类型的容器的提议,但它目前不是标准的一部分。

对于 libstdc ++ libc ++ 中当前实现的非调试容器版本,上面的代码可能会被编译得很好,但它根本不是强制性的。如果成功,迭代器的定义除了指针或对value_type的引用之外不包含任何内容。但是标准中没有要求这样做。

正如您在live example中看到的那样,std::unordered_set存在严重错误,因为其迭代器需要std::hash value_type为完整类型。

由于体系结构(OOP)和性能原因,建议注释中的双重间接可能不是一个好的解决方案。至少看起来很难看std::container3< B * >std::container2< B >,并跟踪不同交叉引用容器的结果动物园的有效性。

迭代器本质上具有指针语义。并且他们不应该要求参考类型的完整性。

如何处理C++14及之前的问题?

3 个答案:

答案 0 :(得分:2)

我使用vectorlistdequesetunordered_set尝试了第二个示例。仅对于unordered_set我得到了一个编译器错误,我可以通过使用指针容器来修复:

 using CA = std::unordered_set< A* >;
 using CB = std::unordered_set< B* >;

参见here(Ideone.com C ++ 14)。

答案 1 :(得分:1)

疯狂的解决方案是存储对齐的存储并手动管理迭代器的对象生命周期,静态断言使存储的大小合适。

这是一种真正的痛苦而且非常不安全(容易犯错误),但它确实有效。

使用无聊容器迭代器的大小/对齐方式。通过adl使用免费功能访问迭代器。

std往往过度需要完全定义的类型。

仔细使用crtp和标签可能会让你自动化危险的代码,但很棘手。

答案 2 :(得分:-1)

在您的实际示例中,基本上您所拥有的是:intdouble之间的关系为1:1,整数存储在vector<int>中,双打存储在unordered_set<double>unordered_map<double, int>

一条路径可能是简化问题,例如:你能将数据放入fs吗?有时这可能是真的,并将解决很多努力。有时这可能不是真的,例如您可能需要按整数对数据进行排序。

我认为使用std 2容器的路径充满了困难,你的编译错误只是冰山一角,例如:

  • 对于1:1的关系,当你添加一个条目时,你需要插入两个容器,所以也许你需要在插入一个时处理这个案例,但另一个失败
  • 迭代器可能会失效(例如,如果你有一个向量元素的迭代器并且你对向量进行排序)
  • [unordered_]集合中的值(以及地图中的[unordered_]键)是const(因此在添加它们后无法更改它们)

另一条路径可能是:考虑使用boost bimap或multi_index之类的东西。他们利用这样一个事实,即如果你想要两个基于节点的结构(例如两个集合)具有1:1的关系,那么对于插入它只需要进行一个包含两个节点的分配