Hi,
Unfortunately, Matt’s suggestion of RTTI will not help you here. It is always illegal to treat a container of Derived* as if it was a container of Base*.
The reason that TArray does not let you do such a conversion is because it is type-unsafe. Imagine the following:
void AddUnrelatedCharacter(TArray<ABaseCharacter*>& Array)
{
Array.Add(GetWorld()->SpawnActor(AUnrelatedCharacter::StaticClass(), ...));
}
void SetUpActors()
{
TArray<AMyCharacter*> Array;
AddUnrelatedCharacter(Array); // X
}
You want line X to compile, but if it did, your TArray would end up holding an AUnrelatedCharacter*.
This is not a limitation of UE4, but a known trait of languages with mutable state:
In addition to Matt’s suggestion of always dealing in TArray and casting on each use, I suggest two other options. You can use explicit constructor syntax to convert your container:
TArray<AMyCharacter*> MyChars;
TArray<ABaseCharacter*> BaseChars(MyChars);
This will make a copy of the array. This may not be desirable. For example: adding a new actor to BaseChars will add it to the copy, not the original array. Also, copying can be slow if you’re having to do it frequently or your array is large.
Alternatively, you can use reinterpret_cast to take advantage of the fact that UObject*s always have the same address as their Super:
void ReadFromArray(const TArray<ABaseCharacter*>& ConstBaseChars);
TArray<AMyCharacter*> MyChars;
const TArray<ABaseCharacter*>& ConstBaseChars = reinterpret_cast<const TArray<ABaseCharacter*>&>(MyChars);
ReadFromArray(ConstBaseChars);
Note that you need to be aware of what you are doing here. This is saying ‘treat the memory of my array as if it was a different type’. We are taking advantage of the fact that TArray is layout-compatible with TArray. This should be fine if you only plan to read from the container, which is why we make it a const reference.
But it is not fine in general:
-
Do not cast away const, or you end up with the same case as I mentioned earlier:
// Bad!
TArray<ABaseCharacter*>& BaseChars = const_cast<TArray<ABaseCharacter*>&>(ConstBaseChars);
AddUnrelatedCharacter(BaseChars);
-
Do not reinterpret_cast if you want an array of a pointer type other than a base UObject:
UCLASS()
class AMyCharacter : public ABaseCharacter, public IBase
{
// …
};
void ReadFromInterfaceArray(const TArray<IBase*>& BaseInterfaces);
// Bad!
const TArray<IBase*>& BaseInterfaces = reinterpret_cast<const TArray<IBase*>&>(MyChars);
ReadFromInterfaceArray(BaseInterfaces);
If in doubt, avoid this construct entirely.
Hope this helps,
Steve