假设sizeof(std :: unordered_map <std :: string,T>)对于所有T都是相同的,实际上是安全的吗?

Tra*_*ian 10 c++ stl language-lawyer c++11

我处于这样一种情况,我在两个类的定义之间有一个循环依赖循环,其中(据我所知)两个类都需要另一个类型才能成为完整类型才能正确定义它们.

简而言之,我需要简化版本的内容:

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_;
};
Run Code Online (Sandbox Code Playgroud)

这种情况实际上比上面更复杂,因为Node它实际上将是一个变体类型(类似于boost::variant),它使用placement new来显式构造预分配中的多种类型的对象之一(并且具有正确的对齐,我忽略了在这个简化中)缓冲区:因此缓冲区不是精确的sizeof(Map),而是依赖于某个计算的常量sizeof(Map).

显然,问题是只在前向声明时才sizeof(Map)可用Map.此外,如果我Node首先将声明的顺序更改为forward declare ,那么编译Map失败,因为std::unordered_map<std::string, Node>Node不完整类型时无法实例化,至少在Ubuntu上使用我的GCC 4.8.2.(我知道这取决于libstdc ++版本比GCC版本更多,但我不知道如何找到它......)

作为替代方案,我正在考虑以下解决方法:

struct Node {
    // some interface...
private:
    // doing this instead of depending on sizeof(Map)
    char buffer[sizeof(std::unordered_map<std::string, void*>)];
    // other stuff...
    void* dummy;
};

struct Map {
    // some interface...
private:
    // this is Map's only member
    std::unordered_map<std::string, Node> map_;
};

// and asserting this after the fact to make sure buffer is large enough
static_assert (sizeof(Map) <= sizeof(std::unordered_map<std::string, void*>),
    "Map is unexpectedly too large");
Run Code Online (Sandbox Code Playgroud)

这基本上依赖于std::unordered_map<std::string, T>对于所有T都具有相同大小的假设,这似乎与我使用GCC的测试相符.

我的问题因此有三个:

  • C++标准中有什么要求这个假设成立吗?(我假设没有,但如果有,我会惊喜......)

  • 如果不是,它是实际上安全的假设它适用于所有合理实现真正反正,和我在修订版的静态断言绝不会火?

  • 最后,还有一个我没想过的更好的解决方法吗?我确信有可能有一些我可以做的事情,而我没有想到,但不幸的是我想不出任何东西......

Yak*_*ont 2

继续假设吧。然后static_assert在施工时你是对的。

还有更奇特的解决方案,例如弄清楚 boost 递归数据结构如何工作并在此处应用该技术(这可能需要编写您自己的映射),或者仅使用boost::支持不完整数据结构的容器。