C ++标准是否允许复制任意多态数据结构?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C ++标准是否允许复制任意多态数据结构?相关的知识,希望对你有一定的参考价值。

我搜索过StackOverflow,但是我找不到直接解决这个问题的问题。

首先是一些上下文:我正在尝试在C ++中实现一个可以处理多态数据的Either类型,就像你可以抛出没有std::runtime_error-keyword的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);
    }
  }

};
答案

实际上,该标准最近添加了一个“平凡可复制”的概念,这样在一个不易于复制的对象上使用memcpy不会产生有效的对象。在引入“平凡可复制”之前,这是由POD-ness控制的。

要制作C ++对象的副本,需要调用其复制构造函数。没有标准的多态方式,但是一些类层次结构选择包含一个满足您需求的虚拟clone()函数(或类似函数)。

您的另一个选择是找到完全避免副本的方法。

以上是关于C ++标准是否允许复制任意多态数据结构?的主要内容,如果未能解决你的问题,请参考以下文章

复制具有未初始化成员的结构

C 和 C++ 编码标准

嵌套结构名称可见性

C语言学习002:第一个完整的C程序代码

《安富莱嵌入式周报》第279期:强劲的代码片段搜索工具,卡内基梅隆大学安全可靠C编码标准,Nordic发布双频WiFi6 nRF7002芯片

是否允许编译器优化浮点常量乘法