One option would be to define your own abstract base class to encapsulate the data. Something like:
template<typename T>
class DataHolder {
public:
virtual ~DataHolder() = default;
virtual gsl::span<T> get() const = 0;
};
Then your function could look something like:
void foo(std::unique_ptr<DataHolder<int>> data) {
if (!data)
return;
for (auto v : data->get())
std::cout << v << " ";
}
The caller can then implement the base class with any container they want to. There will be a small cost of polymophism but not on a per-element basis.
If you don't want to pay for polymorphism, perhaps you could make your function accept a template parameter.
template<typename DataHolder>
void foo(DataHolder data) {
for (auto v : data())
std::cout << v << " ";
}
where the implicit interface for DataHolder
could be satisfied by something like:
struct VectorHolder {
std::vector<int> data;
gsl::span<const int> operator()() const { return data; }
};
or if you really don't want to use vector
. You could use something like this (as suggested by @utnapistim):
struct ArrayHolder {
std::unique_ptr<int[]> data;
ptrdiff_t length;
gsl::span<const int> operator()() const { return {data.get(), length}; }
};