我有以下函数模板:
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的一个模棱两可或不可访问的基类,所以这里没有强制转换可以帮助您。
这是我的代码,但它可以用于我无法控制的类型(即我不能修改MostDerived或HeldAs)。该职能有以下先决条件:
*original是动态类型的MostDerivedHeldAs是MostDerived或MostDerived的直接或间接基类(忽略cv-限定)。*held指的是*original或它的基类子对象之一。让我们假设先决条件已经满足了。在这种情况下,duplicate是否有明确的行为?
C++11 expr.reinterpret.cast说(大胆强调我的观点):
4指针可以显式转换为任何足以容纳指针的整型。映射函数是实现定义的。注意:对于那些知道底层机器的寻址结构的人来说,这并不令人惊讶。-end笔记..。 5积分类型或枚举类型的值可以显式转换为指针。一个指针转换成一个足够大小的整数(如果在实现中存在这样的话),并返回到相同的指针类型,它将有它的原始值;指针和整数之间的映射是实现定义的。注意:除3.7.4.3中描述的情况外,这种转换的结果将不是安全派生的指针值。-end注记
好吧,假设我的编译器是GCC (或者说Clang,因为它使用GCC对实现定义的行为的定义)。引用GCC博士第五章关于C++实现的定义行为:
..。C语言的相应文档中记录了一些选择。见C执行情况。..。
关于第4.7章 (C实现、数组和指针):
将指针转换为整数的结果,反之亦然(C90 6.3.4、C99和C11 6.3.2.3)。 从指针到整数的转换将丢弃大多数重要位(如果指针表示大于整数类型),如果指针表示小于整数类型,则扩展符号,否则比特将保持不变。 从整数到指针的转换如果指针表示小于整数类型,则丢弃最重要的位;如果指针表示大于整数类型,则根据整数类型的签名性进行扩展,否则比特不变。
到现在为止还好。看来,由于我使用的std::uintptr_t保证足够大,可以用于任何指针,而且由于我处理的是相同的类型,所以copyHeld应该指向与held在*original中指向的相同的*copy子对象。
不幸的是,GCC的文档中还有一个段落:
当再次将指针转换为整数和返回时,结果指针必须引用与原始指针相同的对象,否则行为将不定义。也就是说,不能使用整数算法来避免C99和C11 6.5.6/8中禁止的指针算术的未定义行为。
沃姆。因此,现在看来,尽管copyHeld的值是按照前两段的规则计算的,但第三段仍然将其发送到未定义的行为域。
我基本上有三个问题:
duplicate的行为是否未定义?虽然我的问题仅限于GCC (和Clang)在编译器方面的行为,但我希望得到一个考虑各种HW平台的答案,从普通的桌面到异国风格的桌面。
发布于 2014-08-12 23:33:47
通常的模式是在基类中放置一个clone()。
然后,每个派生类都可以实现自己版本的克隆。
class Base
{
public:
virtual Base* clone() = 0;
};
class D: public Base
{
virtual Base* clone(){ return new D(*this);}
};https://stackoverflow.com/questions/25265824
复制相似问题