这个包含 reinterpret_cast 模板类型之间的 C++ 代码定义良好吗?

问题描述

https://onlinegdb.com/RU3bYEfCB

#include <iostream>
using namespace std;

//--------------------Foo------------------
template<int Index>
class Foo {
 public:
  Foo(string first,string second,string third) {
    foo_[0] = first;
    foo_[1] = second;
    foo_[2] = third;
  }

  string operator()() const {
    return foo_[Index];
  }

 private:
  string foo_[3];
};

//---------------------Bar------------------
class BarBase {
 public:
  virtual string operator()() const { return "BarBase"; };
};

template<int Index>
class Bar : public BarBase {
 public:
  Bar(string first,string third) {
    bar_[0] = first;
    bar_[1] = second;
    bar_[2] = third;
  }

  string operator()() const {
    return bar_[Index];
  }

 private:
  string bar_[3];
};

//---------------------Wrapper------------------
class WrapperBase {
 public:
  virtual string operator()() const { return "WrapperBase"; };
};

template<typename T>
class Wrapper : public WrapperBase {
 public:
  Wrapper(T* functor) : functor_(functor) {}
  
  string operator()() const {
    return (*functor_)();
  }
 
 private:
  T* functor_;
};

int main()
{
    Foo<0> foo0("A","B","C");
    Foo<1>& foo1 = *reinterpret_cast<Foo<1>*>(&foo0);
    Foo<2>& foo2 = *reinterpret_cast<Foo<2>*>(&foo0);
    cout<< "foo: " << foo1() << foo2() <<"\n";
    
    Bar<0> bar0("A","C");
    Bar<1>& bar1 = *reinterpret_cast<Bar<1>*>(&bar0);
    Bar<2>& bar2 = *reinterpret_cast<Bar<2>*>(&bar0);
    cout<< "bar: " << bar1() << bar2() <<"\n";
    
    WrapperBase* wrappedfoo0 = new Wrapper<Foo<0>>(&foo0);
    WrapperBase* wrappedfoo1 = new Wrapper<Foo<1>>(&foo1);
    WrapperBase* wrappedfoo2 = new Wrapper<Foo<2>>(&foo2);
    cout<< "wrapped foo: " << (*wrappedfoo1)() << (*wrappedfoo2)() <<"\n";

    return 0;
}

输出

foo: BC
bar: AA
wrapped foo: BC

Foo 和 Bar 完全等价,唯一的区别是 Bar 继承自基类,并且实现的运算符是虚拟的,所以 Bar 有一个函数指针而 Foo 没有。

我想我明白为什么 bar 打印 AA 而 foo 打印 BC (如果我错了,请纠正我)。 这两个类都被实例化了 3 次,每个 operator() 有三个实现,各自的索引硬编码。但是,由于 Bar 有一个函数指针,在 reinterpret_casting from Bar to Bar 之后,虚函数指针仍然指向 Bar

的实现

我想知道这段代码是否定义良好,尤其是在“Foo”和“Wrapped Foo”的情况下。因此,只要我的函子中没有继承,我就可以将它重新解释为另一个 Foo,并且在调用 operator() 时,它将使用当前变量的模板类型的索引(分别为模板类型)调用它包装器被实例化)?

//编辑: 如果 Foo 构造函数被移除(并且 foo_ 成员被公开并从外部初始化),它看起来如何?

那么它应该构成一个 POD 并且标准 (9.2.18) 说明了 reinterpret_cast 和 POD:

一个指向 POD 结构对象的指针,适当地使用 reinterpret_cast,指向它的初始成员(或者如果该成员是一个 位域,然后到它所在的单元),反之亦然。 [ 注意:因此在 POD 结构中可能存在未命名的填充 对象,但不是在其开始时,因为需要实现适当的 对齐。

所以,如果 Foo 构造函数被移除,那么 Foo(和包装的 foo)的行为是否定义良好?

解决方法

Foo 的所有可能实例彼此无关,并且也与 Bar 的所有可能实例无关,它们同样彼此无关。 >

在无关类型之间使用 reintepret_cast 的行为是 undefined

看起来是否相同并不重要。例如,考虑

struct A{}; struct B{};

A a;

reinterpret_cast<B&>(a) 的行为也未定义。