6

我有以下功能模板:

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;
}

目的是复制特定类型的对象并将其返回为与输入相同的子对象“持有”。请注意,原则上,HeldAs可以是 的模棱两可或不可访问的基类MostDerived,因此在这里没有强制转换可以提供帮助。

这是我的代码,但它可以用于我无法控制的类型(即我不能修改MostDerivedor HeldAs)。该函数具有以下前提条件:

  • *original是动态类型的MostDerived
  • HeldAsMostDerived或直接或间接的基类MostDerived(忽略 cv 限定)
  • *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_twhich 保证对于任何指针都足够大,并且因为我正在处理相同的类型,所以copyHeld应该指向as的相同HeldAs子对象。*copyheld*original

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

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

威猛。所以现在看来​​,即使copyHeld是按照前两段的规则计算的值,第三段仍然将其发送到 Undefined-Behaviour 领域。

我基本上有三个问题:

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

  2. 这是哪种未定义行为?“正式未定义,但无论如何都会做你想做的事”的那种,还是“预期随机崩溃和/或自发自焚”的那种?

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

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

4

1 回答 1

0

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

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

class D: public Base
{
        virtual Base*  clone(){  return new D(*this);}
};
于 2014-08-12T23:33:47.237 回答