C++ 从原始数组构造 vector 而不进行复制

标签 c++ memory memory-management

是否可以从原始指针构造一个 vector 而不进行复制?

我知道 vector 本身具有所有权语义,它在构造时分配空间,并负责在销毁时释放该内存。但这不是我想要的,我想做的更像是一个适配器,它对底层数据没有所有权,只是对这些数据进行一些包装,这样:1.更容易使用, 2. 进行一些边界检查。

我想做这样的事情的原因是我的库的潜在用户可能有 eigen、numpy、opencv 或其他东西。我想要一种更灵活的方式来传递数据而无需复制。原始指针可以做到这一点,但我想做一些边界检查,否则别人代码中的越界错误最终会导致我的库内部崩溃。如果我能创建这样的无所有权容器,那么一切就完美了。

最佳答案

正如您提到的,只要您不需要所有权,这就很容易。解决方案是使用类型删除的连续序列。如果在某个时候你确实想要拥有所有权,事情就会变得有点复杂。我已将相关信息添加到此答案的底部。

非拥有

如果您使用 ,您可以使用std::span -- 这是连续数据的非拥有包装器,例如 std::vectorstd::array 等。

如果您使用的是 c++20 之前的任何版本,则始终可以使用预装 span 实现或基于预装 span 实现的实现,例如 gsl::span来自指南支持库,或 bpstd::span来自 BackportC++,即 兼容。

或者找到任何类似 array_view 的类型来完成相同的事情。有许多现有的开源解决方案可以用于此目的。

拥有

如果您确实需要所有权,那么将涉及更多工作 - 特别是如果您希望数据来自不同的连续序列类型。

为此目的,您将需要拥有类型删除,并且在某些时候, vector 将需要移动复制 - 两者之一。如果您可以允许客户端选择移动其 vector ,这将比执行数据的深层复制便宜得多。

为此,您需要有一个接口(interface)和一个类模板实现类型,所有这些都包装在一个漂亮的“vector ”式 API 中。然后,捕获的数据可以间接保存在类模板实现中,位于 unique_ptr 后面:

template <typename T>
class AnyVector
{
public:
   template <typename Container>
   explicit AnyVector(Container&& container)
       : m_container{std::make_unique<Concrete<std::decay_t<Container>>(std::forward<Container>(container))}
   {
   }

   const T& operator[](std::size_t index) const
   {
       return m_container->get(index);
   }
   const T& at(std::size_t index) const
   {
       if (index >= m_container->size()) {
           throw std::out_of_range{"AnyVector<T>::at"};
       }
       return m_container->get(index);
   }
   std::size_t size() const
   {
        return m_container->size();
   }

private:

   // The interface we want all types to follow
   class Interface
   {
   public:
       // Make this API as deep as you need it to be
       virtual ~Interface() = default;
       virtual const T& get(std::size_t index) const = 0;
       virtual std::size_t size() const = 0;
   };

   // The concrete version of the interface, in terms of the underlying container
   template <typename Underlying>
   class Concrete : public Interface
   {
   public:
      template <typename TUnderlying>
      Concrete(TUnderlying&& underlying) 
        : m_underlying{std::forward<TUnderlying>(underlying)}
      {
      }
      
       virtual const T& get(std::size_t index) const override { return m_underlying[index]; }
       virtual std::size_t size() const override { return m_underlying.size(); }
   private:
       Underlying m_underlying;
   }

   std::unique_ptr<Interface> m_container;
};

上述代码仅在输入容器定义了 T::size()T::operator[] 函数时才有效。如果您想要拥有的底层 vector 具有不同名称的函数,那么您需要更有创意地解决这个问题——要么让用户使用类似特征的类型显式指定,要么使用带有 ADL 的非成员函数.

在后一种情况下,可以通过执行以下操作来实现:

    std::size_t size() const override
    {
        // ADL-find 'size'
        return size(m_container);
    }

关于C++ 从原始数组构造 vector 而不进行复制,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63405344/

相关文章:

c++ - 我必须从工厂返回一个指针吗?

c++ - 使 printf 更快地写入 Windows 命令行

c++ - Open CV 和 3D 坐标中的立体校准

c - 理解免费的概念

c++ - 可以在c++中动态和在编译时分配的最大内存

linux - 如何在 Linux 中创建静态程序或进程?

c++ - 在静态对象的析构函数中使用 cout

c++ - 为什么删除不破坏任何东西?

mysql - 将 MySQL innodb 数据库加载到内存中

java - Java VM 是否移动内存中的对象,如果是,如何移动?