C++ 假设sizeof(std::unordered_map<;std::string,T>;)对于所有T都是相同的,实际上是安全的?
我所处的情况是,在两个类的定义之间有一个循环依赖循环,在这种情况下(据我所知),两个类都需要另一个类型为完整类型,以便正确定义它们 简而言之,我需要的是正在发生的事情的简化版本:C++ 假设sizeof(std::unordered_map<;std::string,T>;)对于所有T都是相同的,实际上是安全的?,c++,c++11,stl,language-lawyer,C++,C++11,Stl,Language Lawyer,我所处的情况是,在两个类的定义之间有一个循环依赖循环,在这种情况下(据我所知),两个类都需要另一个类型为完整类型,以便正确定义它们 简而言之,我需要的是正在发生的事情的简化版本: struct Map; struct Node { // some interface... private: // this cannot be done because Map is an incomplete type char buffer[sizeof(Map)]; // p
struct Map;
struct Node {
// some interface...
private:
// this cannot be done because Map is an incomplete type
char buffer[sizeof(Map)];
// plus other stuff...
void* dummy;
};
struct Map {
// some interface...
private:
// this is Map's only member
std::unordered_map<std::string, Node> map_;
};
这基本上依赖于一个假设,即所有T的std::unordered_map
大小相同,这在我使用GCC的测试中似乎是正确的
因此,我的问题有三个:
-
《C++标准》中有什么要求这个假设成立吗?(我假设没有,但如果有,我会惊喜不已……)
- 如果不是,那么假设它适用于所有合理的实现,并且我的修订版本中的静态断言永远不会触发,这实际上安全吗
- 最后,对于这个问题,有没有更好的解决方法,我还没有想到?我相信有可能我可以做一些我没有想到的事情,但不幸的是我想不出任何事情
- 1)否
2) 我不确定
3) 您也可以使用设计模式工厂方法。您的工厂将基于
Map
variant返回对象(编辑:我的意思是您将使用Map variant实例作为参数,工厂方法实现将使用该信息相应地创建返回对象),并且它可以预先分配缓冲区到正确的大小。1)否
2) STL容器不能用不完整的类型实例化。然而,显然有些编译器允许这样做。不允许这样做不是一个微不足道的决定,在许多情况下,你的假设确实是正确的。这篇文章可能会引起你的兴趣。根据标准,如果不添加一层间接层,这个问题是无法解决的,您不想这样做。我只是要提醒一下,你确实没有按照标准做事
话虽如此,我认为您的解决方案是使用stl容器的最佳解决方案。当大小确实超过预期大小时,静态断言确实会发出警告
3) 是的,通过添加另一层间接寻址,我的解决方案如下:
问题是,对象的大小取决于其数组的大小。假设您有一个对象A和对象B:
struct A
{
char sizeof[B]
}
struct B
{
char sizeof[A]
}
对象A将增长,以容纳B大小的字符。但反过来,对象B将不得不增长。我想你能看到这是怎么回事。我知道这正是你的问题,但我认为基本原则非常相似
在这种特殊情况下,我将通过改变
char buffer[sizeof(Map)];
行仅作为指针:
char* buffer
并在初始化后动态分配内存。Sow您的cpp文件将如下所示:
//node.cpp
//untested code
node::node()
{
buffer = malloc(sizeof(map));
}
node::~node()
{
free buffer;
}
1) 可能不会
2) 因为它看起来完全依赖于实现,所以这是一个很大的风险,因为这可能会在任何编译器更新时中断
3) 您可以使用它来接受不完整的类型,从而解决您的问题。继续并假设。然后在构建时断言你是对的
还有一些更奇特的解决方案,比如弄清楚boost递归数据结构是如何工作的,并在这里应用该技术(这可能需要编写自己的映射),或者只使用支持不完整数据结构的
boost::
容器。你能用3澄清你的建议吗?特别是,我不希望通过指针进行比修订版中更多的间接寻址(当static\u assert
通过时,它就可以工作)Node
实际上是一个包含多种类型中的一种而无需间接寻址的联合,可能的类型列表包括Map
、std::string
和bool
。您的Node
实例中有一个会在生命周期内返回一种类型,还是会有所不同?你熟悉设计模式吗?我一次只有一种类型,但它可以改变。如果类型已更改,则节点将显式调用当前内容上的右类型析构函数,并放置新类型的新对象,在过程中更新类型标记。我熟悉设计模式,但不明白您的意思——特别是,您似乎建议工厂返回一些通用指针类型,如void*
,以避免节点
,具体取决于Map
的大小;这正是我不想要的,因为这是一个递归结构,额外的间接操作会很快累积起来。好吧,因为我不认为使用大小可变的char缓冲区来代替引用Map
实例并从该实例获取数据的目的,我只能建议Decorator更改节点
。因为您的建议添加了一个额外的间接层,这是我绝对不想要的。char缓冲区之所以存在,是因为我希望节点
直接在其内部存储对象:节点
可能包含std::string
、bool
,或者其他类型中的一种:其思想是有一个适当对齐的缓冲区,其大小足以容纳最大的对象,实际上可能最终成为Map
。您的建议使得递归遍历此结构的每一步都需要更多的间接性。不,Map
包含std::unordered_Map
,而不是直接节点
,并且前者实际上不会随着节点的大小而增长,因此这种情况与您所说的情况不同。实际上,std::unordered_map
实际上不会随着节点的变化而改变大小,因为它不直接包含节点
;根据value\u类型
pa大小确实发生变化的任何实现
//node.cpp
//untested code
node::node()
{
buffer = malloc(sizeof(map));
}
node::~node()
{
free buffer;
}