5

假设我有以下类模板:

template<typename T>
struct Wrapper {
  T* t_;
  static void check() {
    static_assert(sizeof(Wrapper<T> == sizeof(T*), "Illegal assumption");
  }
};

我查看了 C99 标准和 C++03 标准,但无法为我在static_assert. 我使用几个编译器选项在 Visual C++ 2008 和 2010(32 位)和 linux(64 位)上的 gcc 上进行了尝试,发现我的假设得到了证实。

我的问题是:

  • 我的假设对于 Visual C++ 2008/2010/11 (windows) 是否合理?
  • 对于 gcc 4.* (linux)?
  • 对于编译器标志的任何组合?
  • 你知道这个假设不成立的任何编译器/平台吗?

我猜编译器可能会为结构添加一些填充,例如用于调试目的。但是有没有真正做到这一点的编译器?

编辑:所以正如你在这里问的是我想要实现的:
我有具有以下签名的成员函数:

Someclass* OtherClass::fn();

我想像这样更改签名:

Wrapper<Someclass> OtherClass::fn();

这个包装器就像一些智能指针一样,即它关心指针的生命周期,因此当它超出范围时它会释放它。由于跨 dll 边界调用该函数,我想确保返回的值(现在是一个具体类型,而不仅仅是一个哑指针)在所有情况下(即编译器设置等)与指针大小相同将会。计划/希望是支持调试/发布应用程序/dll 构建的所有组合。
您可能会问:不,我不能使用 boost::shared_ptr<>、std::shared_ptr<>、std::unique_ptr<> 等,因为我们不想将 boost 暴露给 dll 用户,我们还不支持 C++11。

4

3 回答 3

3

如果你想假设它并且你有一个编译时检查,那么继续。大概你会从这样做中获得一些好处。

您无法保证有关填充的任何内容,但通常使用填充来获得对齐,以便该类型的数组使数组的每个成员(以及结构的每个成员)正确对齐。本机指针通常已经是要对齐的正确大小,因此不需要填充,但不能保证这一点。

这不是你可以用 gcc 检查的——它取决于目标架构,而不仅仅是编译器。

于 2012-05-04T12:25:09.727 回答
1

Looking into the standard I found two interesting bits in § 9.2 [class.mem]:

17/ Two standard-layout struct (Clause 9) types are layout-compatible if they have the same number of non-static data members and corresponding non-static data members (in declaration order) have layout-compatible types (3.9).

So, two struct with only a pointer in them would be layout compatible (thus if your assertion holds for this type, it holds for all types).

20/ A pointer to a standard-layout struct object, suitably converted using a reinterpret_cast, points to its initial member (or if that member is a bit-field, then to the unit in which it resides) and vice versa. [ Note: There might therefore be unnamed padding within a standard-layout struct object, but not at its beginning, as necessary to achieve appropriate alignment. —end note ]

In the note we learn that there cannot be padding at the beginning of a structure.


Depending on your goal, 20/ might be sufficient. It means that this works:

void check(T* t) {
    Wrapper<T>* w = reinterpret_cast<Wrapper<T>*>(t);
    assert(w->t_ == t);
}
于 2012-05-04T12:43:54.333 回答
1

我想说您的假设对于大多数编译器和标志都是合理的。

对于任何 T,编译器都能够创建 T 的数组,这些数组需要是连续的,因此它必须能够在没有填充的情况下创建它。因此,它放入结构中的任何填充都是纯可选的,而不是可能需要的。

另一方面,我有理由确定 C 或 C++ 标准都不能保证您的要求。最接近它的保证是因为在 C++ 中它是一个标准布局结构,它限制 C++ 编译器像 C 编译器一样布置字段,因此成员必须按升序排列,开头没有填充-- 但是成员之间和/或最后一个成员之后的填充仍然是允许的。

底线:如果您有指向第一个成员的指针,则可以安全地转换为指向结构的指针(反之亦然)。如果你做一些事情,比如创建一个数组并索引到它,就好像它是另一个数组一样,你就靠自己了——它工作的机会非常好,但我很确定标准不会保证。

于 2012-05-04T12:44:56.517 回答