[英]Does the C++ standard allow for copying an arbitrary polymorphic data structure?
I've searched StackOverflow, but I couldn't find a question that directly addresses this issue.我搜索了 StackOverflow,但找不到直接解决这个问题的问题。
First some context: I'm trying to implement an Either
type in C++ that can handle polymorphic data, much like you can throw a std::runtime_error
without the new
-keyword.首先是一些背景:我试图实现一个
Either
在C类++可以处理多态数据,就像你可以抛出std::runtime_error
没有new
-keyword。 Everything works fine with primitive types, PODs and references, but given that we cannot know the size of a polymorphic data structure upfront, things get more difficult.对于原始类型、POD 和引用,一切都很好,但鉴于我们无法预先知道多态数据结构的大小,事情变得更加困难。 I then thought about copying the structure to a raw buffer on the heap so that I can pass it around as if it was on the stack.
然后我考虑将结构复制到堆上的原始缓冲区,以便我可以像在堆栈上一样传递它。
Example of an Either<L, R>
-type:一个
Either<L, R>
类型的例子:
Either<std::runtime_error, int> doSomeStuff() {
if (err) {
return left(std::runtime_error("Not right!"));
}
return right(42);
}
I experimented with things like std::memcpy(buf, reinterpret_cast<char*>(static_cast<T*>(&value)), sizeof(T))
, but I keep getting SIGSEGV errors.我尝试了
std::memcpy(buf, reinterpret_cast<char*>(static_cast<T*>(&value)), sizeof(T))
,但我不断收到 SIGSEGV 错误。 Is this because, as I suspect, polymorphic structures hold extra bookkeeping that becomes corrupt when copying?这是因为,正如我怀疑的那样,多态结构持有额外的簿记,在复制时会损坏? Is there a way to hold an arbitrary polymorphic structure
T
on the heap so I can pass it as if it were a normal stack-allocated object?有没有办法在堆上保存一个任意的多态结构
T
,这样我就可以像传递一个普通的堆栈分配对象一样传递它? Or is such a thing "undefined" in today's C++ standards?或者这样的事情在今天的 C++ 标准中是“未定义的”吗?
Update: here's the code I have so far.更新:这是我到目前为止的代码。 It's not pretty, but it's the best I've got.
它不漂亮,但它是我拥有的最好的。
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);
}
}
};
Indeed, the standard recently added a concept "trivially copyable", such that using memcpy
on an object which isn't trivially copyable doesn't result in a valid object.事实上,该标准最近添加了一个“可简单复制”的概念,这样在不可简单复制的对象上使用
memcpy
不会产生有效的对象。 Before "trivially copyable" was introduced, this was controlled by POD-ness.在引入“平凡可复制”之前,这是由 POD-ness 控制的。
To make a copy of a C++ object, you need to call its copy constructor.要复制 C++ 对象,您需要调用它的复制构造函数。 There's no standard polymorphic way of doing that, but some class hierarchies choose to include a virtual
clone()
function (or similar) which would meet your need.没有标准的多态方式来做到这一点,但一些类层次结构选择包含一个满足您需要的虚拟
clone()
函数(或类似函数)。
Your other option is to find the way to avoid the copy entirely.您的另一个选择是找到完全避免复制的方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.