2015-10-06 53 views
3

我一直在使用下面的模式将数据与任意对象绑定。每个T对象都位于Wrapper<T>中,附加数据可以从T的自己的地址获得固定偏移量。通过投射第一个字段安全地访问父类

template <class T> 
struct Wrapper 
{ 
    T core; 
    int id; 

    template <class ...Args> 
    Wrapper(int id, Args&&... args) 
     : id(id) 
     , core(std::forward<Args>(args)...) { } 
}; 

template <class T> 
Wrapper<T>& getWrapper(T& core) 
{ 
    return reinterpret_cast<Wrapper<T>&>(core); 
} 

void process(std::string& s) 
{ 
    std::cout << "'" << s << "' - id " 
     << getWrapper(s).id << "\n"; 
} 

int main() 
{ 
    auto *wrapper = new Wrapper<std::string>(188, "Costa Rica"); 

    process(wrapper->core); 
} 

如果我的标准的理解是正确的,这是便携式提供T具有标准的布局。

我的问题是GCC特定的。根据GCC对象模型,我能否假设这在T不是标准布局时也可以使用?

回答

0

正如我确信你怀疑,答案是,你不能。 standard-layout classes的诀窍在于,他们可以安全地用reinterpret_cast转换为指向其第一个非静态数据成员的指针并返回。您正在使用getWrapper()方法执行此操作。

当获得虚拟成员或使用虚拟继承时,标准布局类变为非标准。发生这种情况时,gcc将vtable指针置于零偏移位置,因此您不能再对reinterpret_cast执行第一个数据成员。

在您的实施中,您可能会考虑使用std::is_standard_layout来防止模式的不正确使用。

+0

即使T有一个vtable,为什么这会影响reinterpret_cast?重要的是Wrapper具有“标准”布局(无Vtable),无论T布局如何。请注意,我也假设T的地址与它的vtable指针一致。 –