Mode77
Mode77

Reputation: 1041

May I use templates as a safe solution to handle arrays polymorphically?

It is known that treating items of an array polymorphically is dangerous because, in general, base and derived classes have different sizes, and the pointer arithmetic will most likely fail.

void doSomethingOnBases(Base* bases, int numBases)
{
    for(int i = 0; i < numBases; i++)
    {
        bases[i].doSomething();
    }
}


//...
//...
Derived deriveds[60];    // declare 60 derived objects
doSomethingOnBases(deriveds, 60);    // will crash and burn
//...
//...

But what if, instead, I did this?

template <class X>
void doSomethingOnArray(X* arr, int numItems)
{
    for(int i = 0; i < numItems; i++)
    {
        arr[i].doSomething();
    }
}


//...
//...
Derived deriveds[60];    // declare 60 derived objects
doSomethingOnArray(deriveds, 60);    
//...
//...

In this case, the compiler will see Derived* being passed in and generate doSomethingOnArray(Derived*, int) which is an exact type match, therefore, no pointer arithmetic errors.

If I can guarantee that whatever object is de-referenced in doSomethingOnArray() has a doSomething() method, is this a safe way to fake polymorphism?

Upvotes: 1

Views: 61

Answers (2)

Michael Kenzel
Michael Kenzel

Reputation: 15951

As long as you do it like in your second example and not, e.g., like

doSomethingOnBases<Base>(deriveds, 60);

you should be fine. If you want this to be specific to Arrays, consider writing your template like this:

template <typename X, std::size_t N>
void doSomethingOnArray(X (&arr)[N])
{
    for(auto a = &arr[0]; a != &arr[0] + N; ++a)
        a->doSomething();
}

The advantage of this version is that it cannot really be called incorrectly. It can only be used on actual arrays and it infers the size and element type of the array automatically from the argument.

Also, note that you could simply be using a range-based for loop

for (auto& a : arr)
    a->doSomething();

or

for (Base& b : arr)
    …

if you really need a base class reference for some reason…

Upvotes: 1

Naomi
Naomi

Reputation: 5486

Using templates is possible, but another solution would be to pass a Base** bases where you hold an array of pointers to a objects derived from Base.

void doSomethingOnBases(Base** bases, int numBases)
{
    for(int i = 0; i < numBases; i++)
    {
        bases[i]->doSomething();
    }
}

This would allow you to keep an array of mixed derived objects and not just a single type Derived array.

BTW: Note that if you are going with a templated solution then you don't even need the inheritence from Base.

Upvotes: 2

Related Questions