would be something like
template <typename T> void DoSomething (const T& ref) or void DoSomething(const T& ref, size_t numBytes) or C++20-y void DoSomething (const auto& ref)
If the class you're passing in already qualifies a size like member fn, template<typename T> requires requires(T t){ t.size(); } void DoSomething(const T& x){ ... x.size(); }
> void DoSomething(const uint8_t* p, size_t numBytes)
This is awful you lose type info irreversibly.
> template <typename T> void DoSomething(std::span<T> data)
You can do this but the above examples work just as well.
> Or maybe something even more complicated, like this?
template <typename T, std::size_t N> void DoSomething(std::span<T, N> data)
// Or this? template <typename T, std::size_t N> void DoSomething(std::span<const T, N> data)
This is more explicit, not more complicated...
> In this way, we still keep the clarity and simplicity of the function invocation: > DoSomething(&data, sizeof(data));
Stripping types is not a good idea, especially because you'll run into object lifetime issues _REALLY QUICKLY_. You need to guarantee that the object is trivially copyable.