【问题标题】:C++ construct vector from raw array without copyC ++从原始数组构造向量而无需复制
【发布时间】:2020-08-14 01:40:09
【问题描述】:

是否可以从原始指针构造向量而无需复制?

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

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

【问题讨论】:

  • 您将不得不编写自己的适配器,或者使用您最喜欢的搜索引擎来寻找。
  • 你可以使用std::span,添加绑定检查很容易。

标签: c++ memory memory-management


【解决方案1】:

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

非所有

如果您使用的是,则可以使用std::span——它是一个围绕std::vectorstd::array等连续数据的非所有权包装器。

如果您使用的是 c++20 之前的任何东西,您始终可以使用或基于预装 span 实现的实现,例如 Guideline Support Library 中的 gsl::span 或来自 Guideline Support Library 的 bpstd::span 兼容的 BackportC++。

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

拥有

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

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

要做到这一点,您需要有一个接口和一个类模板实现类型,所有这些都封装在一个漂亮的类似“向量”的 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[] 函数时才有效。如果您想要拥有的底层向量具有不同名称的函数,您将需要更有创意地解决这个问题 - 通过让用户明确指定类似特征的类型,或者使用 ADL 的非成员函数.

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-04-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-30
    • 2017-06-11
    • 2019-03-04
    • 1970-01-01
    相关资源
    最近更新 更多