C++ 重新解释对共享ptr引用的强制转换

C++ 重新解释对共享ptr引用的强制转换,c++,shared-ptr,reinterpret-cast,C++,Shared Ptr,Reinterpret Cast,让B从A类中派生出来。通过阅读各种帖子,我得到了一个印象,我喜欢在A类中扮演角色 const std::shared_ptr<const A> a(new B()); const std::shared_ptr<const B>& b = reinterpret_cast<const std::shared_ptr<const B>&>(a); 出于某种原因,我们不鼓励使用重新解释指针转换。但是,出于性能原因,我希

让B从A类中派生出来。通过阅读各种帖子,我得到了一个印象,我喜欢在A类中扮演角色

    const std::shared_ptr<const A> a(new B());
    const std::shared_ptr<const B>& b = reinterpret_cast<const std::shared_ptr<const B>&>(a);
出于某种原因,我们不鼓励使用重新解释指针转换。但是,出于性能原因,我希望避免创建新的共享ptr。上述代码合法吗?它会导致未定义的行为吗?它似乎在gcc和visualstudio中工作。

您想要的

我高度怀疑上述情况会导致任何性能问题。但是,如果有证据表明共享的_ptr会造成性能问题,那么请退回原始指针:

    const B* pB = static_cast<const B*>(a.get());
另一个提示。请尽量避免在具有继承关系的类之间重新解释\u强制转换。在存在虚拟方法和/或多重继承的情况下,静态转换将正确地将指针偏移调整为正确的vtable或base偏移。但是,重新解释演员阵容是不行的。或者从技术上说:未定义的行为通常会导致UB。有时,出于性能原因,您愿意冒险使用它,但您会尽可能避免这种情况。在这种情况下,最好使用静态\u指针\u强制转换


请注意,即使您不知道,在这种情况下,您可以使用其他哪种类型的强制转换,并且您愿意承担重新解释强制转换的风险,您也必须在强制转换之后和之前使用一些验证-否则您将可能会犯很多错误,并花费大量时间。

首先,您创建一个const std::shared_ptr a类型的对象a,并用指向某个类型B的指针对其进行初始化。这仅在您可以将B*分配给a*时有效,因此应该存在诸如继承之类的关系。忽略这一点,您将使用reinterpret_cast将某种类型的对象转换为对另一种类型的引用:

T1类型的glvalue表达式可以强制转换为“reference to”类型 T2“如果类型为“指向T1的指针”的表达式可以显式 使用重新解释转换转换为类型“指向T2的指针” 结果引用与源glvalue相同的对象,但具有 指定的类型。[注意:也就是说,对于左值,引用类型转换 reinterpret_castx与转换具有相同的效果 *使用内置的&and*运算符重新解释_cast&x,同样地,重新解释_cast&x。-结束注释]

对于指针,reinterpret_cast可归结为转换为void*,然后转换为目标类型:

对象指针可以显式转换为 当对象指针类型的PRV值为 转换为对象指针类型“指向cv T的指针”,结果为 静止的,静止的

这两个静态强制转换的语义定义为:

“指向cv1 void的指针”类型的prvalue可以转换为prvalue 类型为“指向cv2 T的指针”,其中T是对象类型,cv2是 与cv1相同或大于cv1的cv资格。这个 空指针值将转换为 目的地类型。如果原始指针值表示地址 内存中一个字节的A和A满足T的对齐要求, 然后,生成的指针值表示与 原始指针值,即A。任何其他此类操作的结果 未指定指针转换

我正在使用的平台有16位或32位的近指针和远指针。在这种情况下,shared_ptr和shared_ptr类型具有不同的大小和对齐方式,将一种类型转换为另一种类型是未指定的行为。如果对齐匹配,则定义静态强制转换的结果

然而,关于重新解释引用的第一条也包含一个注释

因此,基本上,cast在语义上与指针转换相同,并立即取消引用。即使指针大小相同且对齐方式兼容,使用强制转换的指针也会违反严格的别名规则,因为取消引用是一种访问

如果程序试图访问对象的存储值 通过一个非 以下类型的行为未定义:53 -对象的动态类型, -对象动态类型的cv限定版本, -与4.4中定义的对象动态类型类似的类型, -与对象的动态类型相对应的有符号或无符号类型, -与动态类型的cv限定版本相对应的有符号或无符号类型 对于对象, -在其元素或非静态元素中包含上述类型之一的聚合或联合类型 数据成员,递归地包括子集合的元素或非静态数据成员 或是被控制的联盟


只有在调用时才定义使用共享ptr或任何其他标准类或模板的功能 g函数,包括传递给函数的类型的类的成员函数,包括隐式此参数:

对于任何两种标准类型Foo和Bar,甚至对于用户类型,标准中没有任何内容定义当您调用一个需要Foo并传递Bar的标准函数时会发生什么


这还没有定义;这是未定义的。不满足最基本的先决条件:使用正确类型的参数。

它100%未定义。为什么要这样做?多态性的全部意义不在于不知道动态类型是什么吗?@curiousguy这已经是严格意义上的UB了吗?强制转换是未指定的行为,我认为当您使用指针时,它会得到UB,因为它违反了严格的别名规则。不是说这种吹毛求疵真的很重要,但我很好奇。@Jens不,这是UB,因为没有什么可以定义使用铸造std类的结果。@curiousguy只是铸造不是UB:。这里还引用了访问对象的含义的定义。我不认为这个定义从形式的角度来看是令人满意的。shared_ptr和shared_ptr之间没有继承关系,所以不能使用静态_cast。这些类应该只在它们所持有的指针的类型上有所不同,如果确实如此,那么reinterpret_cast应该是可以的。我有一个内部版本的shared_ptr,它已经与reinterpret_cast合作多年。现在我想切换到标准的共享ptr。但你是对的:我做了一个测试,你的静态指针转换版本和我的一样快。谢谢@德拉甘维多维奇说,这听起来更像是一场灾难,即使它已经工作了多年。重新解释的结果唯一可以保证的是,它可以转换回原始类型。你在重新解释演员阵容时解决了什么问题?如果类是不相关的,我看不出有多少原因让我想对两个对象一视同仁。@DraganVidovic如果A和B是不相关的,如何初始化const std::shared_ptr new B;?这失败了,因为指针类型是不可赋值的:@Jens:如问题中所述,让B派生自类A。它们是相关的;“共享和共享是不相关的。@尼古拉斯谢谢你指出这一点。我误读了注释。对象的动态类型是什么?@curiousguy静态类型是编译时推断的表达式类型,例如声明的变量类型。动态类型是实际对象的类型,即a*a=新B类型的指针指向静态类型为a但动态类型为B的对象。实际对象,而不是什么,虚拟对象?a真正指的是什么?“B对象怎么可能是静态类型的a?”好奇的家伙不知道你在问什么,也不知道你在想象对象。静态和动态类型是标准术语,很容易用谷歌搜索。我再试一次。假设您有一个类a{public:virtualvoidfoo{}和一个类B:public a{}。不,想象一个函数void barA*a{a->foo;}。在编译时,您现在才知道a指向的任何对象都是a类型。但是,您可以调用barnew B,而a指向的对象的实际类型是B,并且将调用B中的实现。这是动态类型,运行时引用对象的类型。@curiousguy我没有想象任何对象,不确定它在我的注释中的位置。我有一种感觉,你在开玩笑,所以请更具体地说,在一个新问题中,静态类型是无意义的?。你能详细说明一下关于指向一个基的指针的问题吗?类型a*的指针a指向给定地址的一个对象,而这个对象是类型B。这就是整点。OIObject看起来是A类型的,即只能使用A的接口。这是静态类型。但它实际上是类型B,所以动态类型是B,方法被解析为B。
    const B* pB = static_cast<const B*>(a.get());
[ Note: That is, for lvalues, a reference
cast reinterpret_cast<T&>(x) has the same effect as the conversion *reinterpret_cast<T*>(&x) with
the built-in & and * operators (and similarly for reinterpret_cast<T&&>(x)). —end note ]