正如您所提到的,只要您不需要所有权,这很容易。解决方案是使用类型擦除的连续序列。如果在某个时候你确实想要拥有所有权,它会变得有点复杂。我已将此信息添加到此答案的底部。
非所有
如果您使用的是c++20,则可以使用std::span——它是一个围绕std::vector、std::array等连续数据的非所有权包装器。
如果您使用的是 c++20 之前的任何东西,您始终可以使用或基于预装 span 实现的实现,例如 Guideline Support Library 中的 gsl::span 或来自 Guideline Support Library 的 bpstd::span与 c++11 兼容的 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);
}