指针/整数算术(un)定义的行为

Rei*_*ica 5 c++ gcc clang undefined-behavior pointer-conversion

我有以下功能模板:

template <class MostDerived, class HeldAs>
HeldAs* duplicate(MostDerived *original, HeldAs *held)
{
  // error checking omitted for brevity
  MostDerived *copy = new MostDerived(*original);
  std::uintptr_t distance = reinterpret_cast<std::uintptr_t>(held) - reinterpret_cast<std::uintptr_t>(original);
  HeldAs *copyHeld = reinterpret_cast<HeldAs*>(reinterpret_cast<std::uintptr_t>(copy) + distance);
  return copyHeld;
}
Run Code Online (Sandbox Code Playgroud)

目的是复制特定类型的对象,并将其与由输入相同的子对象"保持".请注意,原则上,HeldAs可能是一个模糊或不可访问的基类MostDerived,因此没有演员可以在这里提供帮助.

这是我的代码,但它可以用于我控制之外的类型(即我无法修改MostDerivedHeldAs).该函数具有以下前提条件:

  • *original 属于动态类型 MostDerived
  • HeldAs是(MostDerivedMostDerived忽略cv-qualifiation)的直接或间接基类
  • *held指的是*original其基类子对象或其中一个.

让我们假设前提条件令人满意.duplicate在这种情况下是否定义了行为?

C++ 11 [expr.reinterpret.cast]说(大胆强调我的):

4指针可以显式转换为足以容纳它的任何整数类型.映射函数是实现定义的.[ 注意:对于那些了解底层机器的寻址结构的人来说,这并不奇怪.- 注意 ] ......

5可以将整数类型或枚举类型的值显式转换为指针.转换为足够大小的整数(如果实现上存在任何此类)并返回相同指针类型的指针将具有其原始值; 指针和整数之间的映射在其他方面是实现定义的.[ 注意:除3.7.4.3中描述的情况外,这种转换的结果不是安全派生的指针值.- 尾注 ]

好吧,假设我的编译器是GCC(或Clang,因为它使用GCC的实现定义行为的定义).引用GCC文档第5章关于C++实现定义的行为:

...有些选择记录在C语言的相应文档中.参见C实现....

4.7章(C实现,数组和指针):

将指针转换为整数的结果,反之亦然(C90 6.3.4,C99和C11 6.3.2.3).

如果指针表示大于整数类型,则从指针到整数的转换丢弃最高有效位,如果指针表示小于整数类型则进行符号扩展,否则位不变.

如果指针表示小于整数类型,则从整数到指针的强制转换丢弃最高有效位,如果指针表示大于整数类型,则根据整数类型的符号扩展,否则位不变.

到现在为止还挺好.这似乎是因为我使用std::uintptr_t这是保证足够大的任何指针,因为我处理同一类型,copyHeld应指向相同HeldAs的子对象*copyheld指着内*original.

不幸的是,GCC文档中还有一个段落:

当从指针转换为整数并再次返回时,结果指针必须引用与原始指针相同的对象,否则行为是未定义的.也就是说,可能不会使用整数运算来避免指针运算的未定义行为,如C99和C11 6.5.6/8中所禁止的那样.

威猛.所以现在似乎即使copyHeld根据前两段的规则计算值,第三个仍然将其发送到Undefined-Behavior域.

我基本上有三个问题:

  1. 我的阅读是否正确duplicate且未定义的行为?

  2. 这是什么样的未定义行为?"正式未定义,但无论如何都会做你想要的",或"期望随机崩溃和/或自发的自焚"之一?

  3. 如果它确实是未定义的,有没有办法以明确定义的(可能依赖于编译器的)方式做这样的事情?

虽然就编译器而言,我的问题仅限于GCC(和Clang)行为,但我欢迎一个考虑各种硬件平台的答案,从普通桌面到异国情调.

Mar*_*ork 0

通常的模式是将 a 放入clone()基类中。
然后每个派生类都可以实现自己的克隆版本。

class Base
{
     public:
        virtual Base*  clone() = 0;
};

class D: public Base
{
        virtual Base*  clone(){  return new D(*this);}
};
Run Code Online (Sandbox Code Playgroud)