简体   繁体   English

C++ 从未复制的原始数组构造向量

[英]C++ construct vector from raw array without copy

Is it possible to construct a vector from a raw pointer without copy?是否可以从没有复制的原始指针构造向量?

I know the vector itself has ownership semantics, it allocate space when constructed, and responsible for releasing that memory when destroyed.我知道向量本身具有所有权语义,它在构造时分配空间,并在销毁时负责释放 memory。 But that's not what I want, what I want to do is more like an adapter , it has no ownership regarding the underlying data, just make some wrap around that data, so that: 1. easier to use, 2. do some boundry check.但这不是我想要的,我想做的更像是一个adapter ,它对底层数据没有所有权,只是对这些数据进行一些环绕,这样:1. 更容易使用,2. 做一些边界检查.

The reason why I want to do such thing is that potential users of my library might have eigen, numpy, opencv, or something else.我想做这样的事情的原因是我图书馆的潜在用户可能有 eigen、numpy、opencv 或其他东西。 I want to make a more flexible way to pass data without copy.我想用一种更灵活的方式来传递数据而不复制。 Raw pointer can do it, but I want to do some boundry check, otherwise out-of-bound error in someone else's code will finally lead to a crash inside my library.原始指针可以做到,但我想做一些边界检查,否则其他人代码中的越界错误最终会导致我的库内崩溃。 If I can create such no-ownership container then everything would be perfect.如果我可以创建这样的无所有权容器,那么一切都将是完美的。

This is quite easy as long as you don't require ownership, as you mention.正如您所提到的,只要您不需要所有权,这很容易。 The solution is to use a type-erased contiguous sequence.解决方案是使用类型擦除的连续序列。 If at some point you do desire ownership, it gets a bit more complicated.如果在某个时候你确实想要拥有所有权,它会变得有点复杂。 I've added information on this to the bottom of this answer.我已在此答案的底部添加了有关此信息的信息。

Non-owning非所有

If you are using , you can make use of std::span -- which is a non-owning wrapper around contiguous data such as std::vector , std::array , etc.如果您使用的是 ,则可以使用std::span ——它是一个围绕连续数据的非拥有包装器,例如std::vectorstd::array等。

If you are using anything prior to c++20, you can always use or base an implementation off of a pre-canned span implementation, such as gsl::span from the Guideline Support Library, or bpstd::span from BackportC++ which is compatible.如果您使用的是 c++20 之前的任何东西,您始终可以使用或基于预装span实现的实现,例如 Guideline Support Library 中的gsl::span或 BackportC++ 中的bpstd::span ,即兼容。

Or alternatively find any array_view -like type which accomplishes the same thing.或者找到任何完成相同事情的类似array_view的类型。 There are many existing open-source solutions that can be leveraged for this purpose.有许多现有的开源解决方案可以用于此目的。

Owning拥有

If you do require ownership, then more work will be involved -- especially if you want the data to come from different contiguous sequence types.如果您确实需要所有权,那么将涉及更多工作——特别是如果您希望数据来自不同的连续序列类型。

You will require owning type-erasure for such a purpose, and at some point a vector will either need to be moved or copied -- one of the two.为此,您将需要拥有类型擦除,并且在某些时候需要移动复制向量 - 两者之一。 If you can allow the clients the option to move their vectors, this would be much cheaper than performing a deep copy of the data.如果您可以允许客户选择移动他们的向量,这将比执行数据的深层复制便宜得多。

To do this, you will need to have an interface, and a class-template implementation type, all wrapped into a nice "vector"-like API.为此,您需要有一个接口和一个类模板实现类型,所有这些都包装在一个漂亮的“向量”中,如 API。 The captured data can then be held in the class-template implementation indirectly, behind a unique_ptr :然后可以将捕获的数据间接保存在类模板实现中,在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;
};

The above code only works if the input container defines a T::size() and T::operator[] function.上述代码仅在输入容器定义了T::size()T::operator[] function 时才有效。 If the underlying vectors you want to own have functions that are differently named, you will need to be more creative in solving this -- either through having the user explicitly specify with a traits-like type, or alternatively using non-member functions with ADL.如果您想要拥有的底层向量具有不同名称的函数,您将需要更有创意地解决这个问题 - 通过让用户明确指定类似特征的类型,或者使用 ADL 的非成员函数.

In the latter case, this could be achieved by doing something like:在后一种情况下,这可以通过执行以下操作来实现:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM