【发布时间】:2021-08-25 13:41:14
【问题描述】:
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 second, 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", "B", "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 从 Bar 到 Bar 之后,虚函数指针仍然指向 Bar
我想知道这段代码是否定义良好,尤其是在“Foo”和“Wrapped Foo”的情况下。因此,只要我的仿函数中没有继承,我就可以将它重新解释为另一个 Foo 并且在调用 operator() 时它将使用当前变量的模板类型的索引(分别是模板类型)调用它包装器被实例化了)?
//编辑: 如果 Foo 构造函数被移除(而 foo_ 成员改为公开并从外部初始化),它会是什么样子?
那么它应该构成一个 POD 并且标准 (9.2.18) 说 reinterpret_cast 和 POD:
指向 POD 结构对象的指针,使用 reinterpret_cast,指向它的初始成员(或者如果该成员是 位域,然后到它所在的单元),反之亦然。 [ 注意:因此在 POD 结构中可能存在未命名的填充 对象,但不是在开始时,因为有必要实现适当的 对齐。
那么,如果 Foo 构造函数被移除,那么 Foo(和包装的 foo)的行为是否定义良好?
【问题讨论】:
-
reinterpret_cast的大多数用法都会导致 UB,包括这个。 -
Foo<T>是与Foo<U>完全不同的类型。这个问题基本上归结为将long long*重新解释为double*是否安全,不,它是UB。 -
注意:
F<0>、F<1>、F<2>是不相关的类。
标签: c++ inheritance virtual-functions reinterpret-cast