简体   繁体   English

几乎pod数据的reinterpret_cast(布局兼容性足够)

[英]reinterpret_cast for almost pod data (is layout-compatibility enough)

I am trying to learn about static_cast and reinterpret_cast . 我想了解static_castreinterpret_cast

If I am correct the standard (9.2.18) says that reinterpret_cast for pod data is safe: 如果我是正确的,标准(9.2.18)说pod数据的reinterpret_cast是安全的:

A pointer to a POD-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. 指向POD结构对象的指针,使用reinterpret_cast适当转换,指向其初始成员(或者如果该成员是位字段,则指向它所在的单元),反之亦然。 [ Note: There might therefore be unnamed padding within a POD-struct object, but not at its beginning, as necessary to achieve appropriate alignment. [注意:因此,在POD-struct对象中可能存在未命名的填充,但不是在其开头,以实现适当的对齐。 — end note ] - 结束说明]

My question is how strictly to interpret this. 我的问题是如何严格解释这一点。 Is, for example, layout-compatibility enough? 例如,布局兼容性足够吗? and if not, why not? 如果没有,为什么不呢?

To me, the following example shows an example where a strict 'only POD is valid' interpretation seems to be wrong. 对我来说,以下示例显示了严格的“仅POD有效”解释似乎是错误的示例。

class complex_base  // a POD-class (I believe)
{
public:  
  double m_data[2];
};

class complex : public complex_base
{  //Not a POD-class (due to constructor and inheritance)
public:
  complex(const double real, const double imag); 
}

double* d = new double[4];
//I believe the following are valid because complex_base is POD
complex_base& cb1 = reinterpret_cast<complex_base&>(d[0]);  
complex_base& cb2 = reinterpret_cast<complex_base&>(d[2]);
//Does the following complete a valid cast to complex even though complex is NOT POD?
complex& c1 = static_cast<complex&>(cb1);
complex& c2 = static_cast<complex&>(cb2);

Also, what can possibly break if complex_base::m_data is protected (meaning that complex_base is not pod)? 另外,如果complex_base::m_data受到保护(意味着complex_base不是pod),那么可能会破坏什么? [EDIT: and how do I protect myself/detect such breakages] [编辑:我如何保护自己/检测此类破损]

It seems to me that layout-compatibility should be enough - but this does not seem to be what the standard says. 在我看来,布局兼容性应该足够了 - 但这似乎不是标准所说的。

EDIT: Thanks for the answers. 编辑:谢谢你的答案。 They also helped me find this, http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2342.htm 他们也帮助我找到了这个, http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2342.htm

I believe the following are valid because complex_base is POD 我相信以下内容是有效的,因为complex_base是POD

You are wrong. 你错了。 d[0] does not refer to the first member of a complex_base object. d[0]不引用complex_base对象的第一个成员。 Its alignment may therefor not be good enough for a complex_base object, therefor such a cast is not safe (and not allowed by the text you quote). 因此,对于complex_base对象来说,它的对齐可能不够好,因此这样的强制转换是不安全的(并且你引用的文本不允许)。

Does the following complete a valid cast to complex even though complex is NOT POD? 即使复杂不是POD,以下是否完成了对复杂的有效转换?

cb1 and cb2 do not point to subobjects of an object of type complex , therefor the static_cast produces undefined behavior. cb1cb2不指向complex类型的对象的子对象,因此static_cast产生未定义的行为。 Refer to 5.2.9p5 of C++03 参见C ++ 03的5.2.9p5

If the lvalue of type "cv1 B" is actually a sub-object of an object of type D, the lvalue refers to the enclosing object of type D. Otherwise, the result of the cast is undefined. 如果类型“cv1 B”的左值实际上是类型D的对象的子对象,则左值引用类型D的封闭对象。否则,未定义强制转换的结果。

It's not enough if merely the types involved fit together. 仅仅涉及的类型合在一起是不够的。 The text talks about a pointer pointing to a POD-struct object and about an lvalue referring to a certain subobject. 该文本讨论了指向POD结构对象的指针以及指向某个子对象的左值。 oth complex and complex_base are standard-layout objects. 复杂和complex_base是标准布局对象。 The C++0x spec says, instead of the text you quoted: C ++ 0x规范说,而不是你引用的文字:

Is POD-ness requirement too strict? POD-ness要求是否过于严格?

This is a different question, not regarding your example code. 这是一个不同的问题,不是关于您的示例代码。 Yes, requiring POD-ness is too strict. 是的,要求POD-ness过于严格。 In C++0x this was recognized, and a new requirement which is more loose, "standard-layout" is given. 在C ++ 0x中,这被认可,并且给出了一个更宽松的新要求“标准布局”。 I do think that both complex and complex_base are standard-layout classes, by the C++0x definition. 我认为complex complex_base都是标准布局类,由C ++ 0x定义。 The C++0x spec says, instead of the text you quoted: C ++ 0x规范说,而不是你引用的文字:

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. 指向标准布局结构对象的指针(使用reinterpret_cast进行适当转换)指向其初始成员(或者如果该成员是位字段,则指向它所在的单元),反之亦然。

I interpret that as allowing to cast a pointer to a double , which actually points to a complex member (member by inheritance), to be casted to a complex* . 我将其解释为允许将指针强制转换为double ,实际上指向complex成员(通过继承成员),将其转换为complex* A Standard-layout class is one that either has no base classes containing non-static data, or has only one base-class containing non-static data. 标准布局类是没有包含非静态数据的基类,或者只有一个包含非静态数据的基类的类。 Thus there is an unique "initial member". 因此,有一个独特的“初始成员”。

What can break is that non-POD class instances may have vtable pointers, in order to implement virtual dispatch, if they have any virtual functions, including the virtual dtor. 可以打破的是,非POD类实例可能具有vtable指针,以便实现虚拟分派,如果它们具有任何虚拟功能,包括虚拟dtor。 The vtbl pointer will typicaly be the first member of the non-POD class. vtbl指针通常是非POD类的第一个成员。

(Technically, virtual dispatch doesn't have to implemented this way; practically it is. That's why the Standard has to be so strict about what qualifies as a POD type.) (从技术上讲,虚拟调度不必以这种方式实现;实际上就是这样。这就是为什么标准必须严格对待什么是POD类型。)

I'm honestly not sure why just having a ctor ("8.5.1(1): "An aggregate is an array or class (clause 9) with no user-declared constructors (12.1)") disqualifies something from being a POD. But, it does. 老实说,我不确定为什么只有一个ctor(“8.5.1(1):”聚合是一个数组或类(第9节),没有用户声明的构造函数(12.1)“)取消了POD的资格。但是,确实如此。

In the particular case you have here, of course, there's no need for a reinterpret cast. 当然,在特殊情况下,您无需重新解释演员阵容。 Instead, just add a conversion operator to the base class: 相反,只需将转换运算符添加到基类:

class complex_base  // a POD-class (I believe)
{
public:  
  double m_data[2];
  operator double*() {
    return m_data;
  }
};


complex_base b; // create a complex_base
double* p = b; 

Since a complex_base isn't a double*, the C++ compiler will apply one (and only one) user-defined conversion operator in order to assign b to p. 由于complex_base不是double *,因此C ++编译器将应用一个(且仅一个)用户定义的转换运算符,以便将b分配给p。 That means that p = b invokes the conversion operator, yielding p = b.operator double*() (and note that that's actually legal syntax -- you can directly call conversion operators, not that you should), which of course does whatever it does, in this case return m_data. 这意味着p = b调用转换运算符,产生p = b.operator double*() (并注意这实际上是合法的语法 - 你可以直接调用转换运算符,而不是你应该的),这当然可以做任何事情在这种情况下,返回m_data。

Note that this is questionable, as we now have direct access to b's internals. 请注意,这是有问题的,因为我们现在可以直接访问b的内部。 In practice, we might return a const double*, or a copy, or a smart copy-on-write "pointer" or .... 在实践中,我们可能会返回一个const double *,或者一个副本,或者一个智能的写时复制“指针”或....

Of course, in this case, m_data is public anyway, so we're not worse off than if we just wrote: 当然,在这种情况下,m_data无论如何都是公共的,所以我们并没有比我们写的更糟糕:

 double* p = b.m_data;

We are a bit better off, actually, because clients of complex_base don't need to know how to convert it to a double. 实际上,我们有点好过,因为complex_base的客户端不需要知道如何将它转换为double。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM