There are many ways on doing it: using shared pointers, using references, using out arguments instead of a return value (then you can return true/false depending on the result, this is just add a simple branch later). The best way is to check for examples in the engines git repo. This way you will see in which use cases Epic uses which procedure.
So this simple approach should work for you and thanks to RVO it’s fast too:
TArray<UUnitCombatModule*> attackableUnits;
// Filling the array somehow
return attackableUnits;
it is also short and keeps your interface clean.
But you may get problems if your array is not temporary, because the pointers inside the TArray may transform into an invalid state between frames (if an Actor gets destroyed for example). If you need this TArray over multiple frames you should consider Omnicypher’s solution and annotate the TArray attribute with an UProperty annotation.
If anyone wonders how to do it in BP functions, just drag the variable to return node and it will automatically create an output pin. also works for other types (not only arrays)
if you don’t have a variable, just create one to make the output and delete afterwards, or use makeArray node for that :]
One possibility is to pass array as reference instead of returning it.
This leaves allocation concerns to the caller, and avoids unnecessary copies.
The downside is it makes calling the function on C++ side a bit more annoying as you have to declare the array to pass it to the function.