[英]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.我已在此答案的底部添加了有关此信息的信息。
If you are using c++20 , you can make use of std::span
-- which is a non-owning wrapper around contiguous data such as std::vector
, std::array
, etc.如果您使用的是c++20 ,则可以使用std::span
——它是一个围绕连续数据的非拥有包装器,例如std::vector
、 std::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 c++11 compatible.如果您使用的是 c++20 之前的任何东西,您始终可以使用或基于预装span
实现的实现,例如 Guideline Support Library 中的gsl::span
或 BackportC++ 中的bpstd::span
,即c++11兼容。
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.有许多现有的开源解决方案可以用于此目的。
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.