【问题标题】:How to do a static assert that a pointer cast is trivial?如何做一个静态断言指针转换是微不足道的?
【发布时间】:2011-07-28 22:55:34
【问题描述】:

假设我有这些类型:

struct A {
    int a;
};

struct B {
    int b;
};

struct C : public A, public B {
    int c;
};

C* 指针可以转换为 A* 指针,而无需调整实际地址。但是当C* 被强制转换为B* 时,该值必须改变。我想确保我拥有的两个相关类型可以相互转换而不会改变地址(即没有多重继承,或者基类是派生类的第一个基类)。这可以在运行时检查,例如像这样

assert(size_t(static_cast<A*>((C*)0xF000) == 0xF000);
assert(size_t(static_cast<B*>((C*)0xF000) != 0xF000);

这行得通。但是这些信息在编译时是已知的,所以我正在寻找一种方法来对其进行编译时断言。将上述内容转换为静态断言的明显方法(例如,将 assert 替换为 BOOST_STATIC_ASSERT 会给出错误“转换为整数或枚举类型以外的类型不能出现在常量表达式中”使用 g++ 4.2。

可移植性并不太重要。使用 gcc 扩展或 hacky 模板技巧都可以。

更新:发现之前问过几乎一样的问题:C++, statically detect base classes with differing addresses?。使用offsetof() 也是唯一有用的建议。

【问题讨论】:

  • 不想成为吹毛求疵的人,但既然确切的内存布局是一个实现细节......你为什么会对这个感兴趣?
  • 我很确定 Boost/lambda 库包含这方面的内容。我必须查一下,但 Alexandrescu 和 Vandervoorde 已经发表了该领域的几乎所有技巧
  • 我对@Matthieu M. 提出的问题真的很感兴趣:在什么情况下演员可以是微不足道的? (即,如果它不是微不足道的,那么成本很可能可以忽略不计:向派生指针添加一个常量,如果条件没有正确完成,您将在尝试调试看起来像是损坏的内存时遇到真正的痛苦)跨度>
  • 说来话长。这是一个事件系统,我想使用类型擦除来减少模板膨胀。例如。我想将 f(T*) 类型的函数指针(对于许多不同的 T)视为 f(void*),以将它们存储在通用结构中。进一步的细节很复杂。关键是有很多重复的模板化代码实际上是相同的,因为演员表是微不足道的(在大多数情况下,我正在处理,实际上是这样)。我试图避免重复生成的相同代码,但让编译器检查我没有搞砸:)
  • @DS,您可能希望检查链接器的设置。一些链接器能够检测和合并相同的函数,因此即使您的编译器生成了许多重复项,您正在进行的合并它们的工作可能是计算机可以为您完成的工作.

标签: c++ pointers casting static-assert


【解决方案1】:

“我想确保两个相关类型可以相互转换而不改变地址(即没有多重继承,或者基类是派生类的第一个基类)。”

你的“即”不正确。例如,Derived 的前 4 个字节完全有可能是一个 vtable 指针,即使 Base 不是多态的。是的,如果 (1) 第一个基本子对象在偏移量 0 处,并且 (2) vtable 指针在偏移量 0 处,C++ 编译器会更容易。但这两个目标本质上是不一致的,没有明显更好的选择。

现在,第一部分可以在理论上进行测试。对于标准布局类型,offset_of(Base, first_member)offset_of(Derived, first_member) 没有区别。但实际上,offset_of 不适用于有趣的类型;是UB。这个检查的重点是检查一个类型,所以对于非标准布局类型,它应该可靠地失败。

【讨论】:

  • “你不能检查,因为它不是真的”。我的意思是,由于它并不总是正确的,我需要能够检查!现在,offsetof(C,a) 的想法听起来很有希望,但 gcc 抱怨“对 NULL 对象的非静态数据成员 'A::a' 的无效访问。”
  • 其实gcc的投诉是警告,不是错误。因此,当 Base 的第一个成员已知时,这确实有效。那是东西。谢谢!
  • 我已经编辑了 MSalters 的评论“你不能检查,因为这不是真的”到 I-think-clearer “你的‘即’不正确”。我不认为 MSalters 可能一直试图说你的整个陈述是错误的。根据他的下一句话,我认为他的意思是“可以强制转换两个相关的类型......”实际上并不真正等同于“没有多重继承......”。
【解决方案2】:

根据 MSalters 的建议和 C++, statically detect base classes with differing addresses? 的回答,这是我能想到的最接近答案的内容。它可能是 gcc 特定的,并且需要知道基类的 some 成员:

#pragma GCC diagnostic ignored "-Winvalid-offsetof"     // To suppress warning.
BOOST_STATIC_ASSERT(offsetof(C, a) == offsetof(A, a));
BOOST_STATIC_ASSERT(offsetof(C, b) != offsetof(B, b));
#pragma GCC diagnostic warn "-Winvalid-offsetof"

显然这既不方便又可怕(需要认识会员并关闭警告)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多