Assume we have an Objective-C NSArray of instances of either the Shoe class or the Computer class. Both of these classes have the method age.
Objective-C would allow you to obtain the ages of all the shoes and computers in one simple statement like this:
NSArray *agesOfShoesAndComputers = [ourArray valueForKey:@"age"];
Does C++ have any similar methodology that can be applied to, say, vectors, or will stuff like this necessarily always be somewhat more cumbersome in C++?
Firstly, std::vector<T> is statically typed. That is, you need to know whether it contains shoes, or computers, at compile time.
So, the short answer is that it'll always be more cumbersome in C++ (static typing is really an optimization, so this is the price we pay for performance).
If you want to use traditional runtime polymorphism, you need a common base class:
class MyBase
{
public:
virtual ~MyBase();
virtual int age() = 0;
};
class Shoe: public MyBase
{
public:
virtual int age();
};
// same for Computer
now I can have
std::vector<std::unique_ptr<MyBase>> ourArray;
and
std::vector<int> ages(std::vector<std::unique_ptr<MyBase>> const &ourArray)
{
std::vector<int> ages;
std::transform(ourArray.begin(), ourArray.end(),
std::back_inserter<int>(ages),
[](std::unique_ptr<MyBase> const& elem) { return elem->age(); }
);
return ages;
}
The call to elem->age() here relies on the common interface defined by MyBase, but dispatches to the derived class implementations (Shoe::age() and Computer::age() respectively).
It's less flexible than the Objective C version, because your common interface, inheritance etc. are all still statically defined.
Note that if this is a simple property, you could just skip the virtual method call entirely and put it in MyBase instead. Shoes and Computers will still need to inherit from this class, but they can just set the property value in the base class.
You may be able to use compile-time polymorphism instead, ie, templates. This is in some ways more similar to the Objective C approach, because it works for any type that supports the expected method, irrespective of inheritance relationships.
template <typename T> // T could be Shoe, Computer or anything else
std::vector<int> ages(std::vector<T> const &ourArray)
{
std::vector<int> ages;
std::transform(ourArray.begin(), ourArray.end(),
std::back_inserter<int>(ages),
[](T const& elem) { return elem.age(); }
);
return ages;
}
the mechanism is completely different though - you still have statically distinct call sites with either vector<Shoe> or vector<Computer> or whatever, and never the twain shall meet.
C++ is somewhat less dynamic, but what you're looking for is generally transform:
vector<unique_ptr<BaseClassForShoeAndComputer>> ourArray;
vector<int> agesOfShoesAndComputers;
transform(ourArray.begin(), ourArray.end(),
back_inserter(agesOfShoesAndComputers),
[](unique_ptr<BaseClassForShoeAndComputer>& s) { return s->age(); });
However, you can't really have unrelated objects inside a single container (well, you can, but it's another thing to use them); you can however, use templates (unfortunately, not with lambdas) to achieve static duck typing (for use with any type with an "age" method).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With