是否可以从原始指针构造一个 vector 而不进行复制?
我知道 vector 本身具有所有权语义,它在构造时分配空间,并负责在销毁时释放该内存。但这不是我想要的,我想做的更像是一个适配器,它对底层数据没有所有权,只是对这些数据进行一些包装,这样:1.更容易使用, 2. 进行一些边界检查。
我想做这样的事情的原因是我的库的潜在用户可能有 eigen、numpy、opencv 或其他东西。我想要一种更灵活的方式来传递数据而无需复制。原始指针可以做到这一点,但我想做一些边界检查,否则别人代码中的越界错误最终会导致我的库内部崩溃。如果我能创建这样的无所有权容器,那么一切就完美了。
最佳答案
正如您提到的,只要您不需要所有权,这就很容易。解决方案是使用类型删除的连续序列。如果在某个时候你确实想要拥有所有权,事情就会变得有点复杂。我已将相关信息添加到此答案的底部。
非拥有
如果您使用c++20 ,您可以使用std::span
-- 这是连续数据的非拥有包装器,例如 std::vector
、std::array
等。
如果您使用的是 c++20 之前的任何版本,则始终可以使用预装 span
实现或基于预装 span
实现的实现,例如 gsl::span
来自指南支持库,或 bpstd::span
来自 BackportC++,即 c++11兼容。
或者找到任何类似 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/