我已经搜索了StackOverflow,但是找不到直接解决此问题的问题。
首先要介绍一下上下文:我正在尝试在C ++中实现一个可以处理多态数据的Either
类型,就像您可以在没有std::runtime_error
关键字的情况下抛出new
一样。一切与原始类型,POD和引用都可以正常工作,但是鉴于我们无法预先知道多态数据结构的大小,因此事情变得更加困难。然后,我考虑将结构复制到堆上的原始缓冲区,以便可以将其传递给 ,就像它在堆栈上一样。
Either<L, R>
类型的示例:
Either<std::runtime_error, int> doSomeStuff() {
if (err) {
return left(std::runtime_error("Not right!"));
}
return right(42);
}
我尝试了类似std::memcpy(buf, reinterpret_cast<char*>(static_cast<T*>(&value)), sizeof(T))
之类的事情,但始终出现SIGSEGV错误。我怀疑这是因为多态结构拥有额外的簿记功能,而簿记功能在复制时会损坏吗?有没有一种方法可以在堆上保留任意多态结构T
,这样我就可以像将其当作普通的堆栈分配对象一样传递它了?还是在当今的C ++标准中这种事情“未定义”?
更新:这是到目前为止的代码。这不漂亮,但这是我所拥有的最好的。
struct ConstBoxRefTag { };
struct BoxMoveTag { };
struct PlainValueTag { };
// struct BoxValueTag { };
template<typename T>
struct GetTag { using type = PlainValueTag; };
template<typename T>
struct GetTag<const Box<T>&> { using type = ConstBoxRefTag; };
template<typename T>
struct GetTag<Box<T>&&> { using type = BoxMoveTag; };
template<typename T>
struct GetTag<Box<T>> { using type = ConstBoxRefTag; };
template<typename T>
class Box<T, typename std::enable_if<std::is_polymorphic<T>::value>::type> {
void* buf;
size_t sz;
template<typename R, typename Enabler>
friend class Box;
public:
using Type = T;
template<typename R>
Box(R val): Box(typename box::GetTag<R>::type {}, val) {}
template<typename R>
Box(ConstBoxRefTag, R oth): buf(std::malloc(oth.sz)), sz(oth.sz) {
std::memcpy(buf, oth.buf, oth.sz);
}
template<typename R>
Box(BoxMoveTag, R oth): buf(std::move(oth.buf)), sz(std::move(oth.sz)) {
oth.buf = nullptr;
};
template<typename R>
Box(PlainValueTag, R val): buf(std::malloc(sizeof(R))), sz(sizeof(R)) {
std::memcpy(buf, reinterpret_cast<void*>(static_cast<T*>(&val)), sizeof(R));
}
template<typename R>
R as() const {
static_assert(std::is_base_of<T, R>::value, "Class is not a subtype of base class");
return *static_cast<const R*>(reinterpret_cast<const T*>(&buf));
}
T& reference() {
return *reinterpret_cast<T*>(&buf);
}
const T& reference() const {
return *static_cast<T*>(&buf);
}
~Box() {
if (buf != nullptr) {
reference().~T();
std::free(buf);
}
}
};
答案 0 :(得分:1)
实际上,该标准最近添加了一个“普通可复制”的概念,因此在不可普通复制的对象上使用memcpy
不会得到有效的对象。在引入“普通复制”之前,这是由POD-ness控制的。
要复制C ++对象,您需要调用其复制构造函数。没有标准的多态方法可以执行此操作,但是某些类层次结构选择包含满足您需求的虚拟clone()
函数(或类似函数)。
您的另一选择是找到完全避免复制的方法。