I'll make several semi-orthogonal recommendations:
Recommendation 1: Clarify the return type with a type definition
I need it to be known that this method returns this list
So, do that! Instead of declaring the function as returning auto:
- Factor out the lambda - either into a static private member function, or a function in a
detail_
namespace etc. Now you can use it, or its type, without repeating it. Suppose you've defined it as called foo()
- Write something like:
using bar = std::decltype(dataStorage | std::ranges::views::transform(foo));
and of course you will replace bar
with the type you want the users of this method to know the result by.
Note: 康桓瑋's answer suggests using a constrained auto
, for a similar effect. That's a good idea, possibly even better than this one (although slightly more verbose). So please upvote their answer.
Recommendation 2: Carefully consider the need for inheritance
Before you even consider the "bigger" DataWrapper
class, holding the vector - perhaps you should think about whether you really need to use MyInterface
rather than Data
. You seem to be relying to some extent on actually returning Data
's from getData()
- hence the name.
- Do you have other inheritors of
MyInterface
? If not, consider dropping it (or keeping it, but not using it other than to streamline the way you define Data
).
- Are there many inheritor classes of
MyInterface
? Or are they not known at the point of definition of MyInterface
? If the answer is "no", consider using an std::variant<Data,C1,C2,C3>
for the case of C1
, C2
, C3
being the inheritors of MyInterface
.
- Can you templatize the code accessing MyInterface child classes? You might make MyInterface into a concept rather than a base class. Or CRTP might come into play somehow.
If you can do any of those, it's possible you could avoid the need for a DataWrapper
class _completely, and simply have an std::vector<foo>
for some relevant foo (Data
, or the variant, class, or even std::any
).
I'm not saying that avoiding inheritance is "better"; it means switching from one tradeoff of benefits and detriments to another. But many people just opt for inheritance because it's the more traditional way of doing things in C++ rather than being what best suits their needs.
Recommendation 3: Don't return a container (or range), be the container!
You have a value-type class (DataWrapper
) with a vector of data. Now, you're not willing to let people use that vector directly, so DataWrapper
can't be used as a C++ container of Data
s.
Well - exploit that! Make it usable as a container of objects-inheriting-MyInheritance
's, i.e. MyInheritance
references! Implement begin, cbegin, size, empty, iterator and all the other parts of that named requirement. Perhaps go as far as implementing everything in ContiguousContainer... while that will make your class somewhat more complex, you will actually be saving the indirect complexity of using std::ranges
(whole lot of code under the hood there), plus - your users won't have to know about getData()
, so use-wise, you're probably simplifying things.
auto
. – Deemsterconcept
to describe this (as top answer has taught me) seems like the best of all worlds. – Streptothricinauto
is that a user mistakes what type gets returned and the code still compiles (e.x. I pass someauto
return function tostd::cout
and I print the class rather than the member). – Streptothricin