'TDelegate<bool (float),FDefaultDelegateUserPolicy>::CreateUObject': no overloaded function could convert all the argument types

Hi!

I’m using FTSTicker in a GameInstanceSubsystem and I need to pass more arguments, TArray and TSubclassOf<ASLStarActor>, to the method used in `FTickerDelegate`.

To do it I have tried this:

Source file:

FTickerDelegate TickerDelegateHandler =
     FTickerDelegate::CreateUObject(
          this,
          &USMyGameInstanceSubsys::HandleTickerDelegate,
          Stars,
          BlueprintStar);

 FTSTicker::FDelegateHandle TickDelegateHandle = FTSTicker::GetCoreTicker().AddTicker(TickerDelegateHandler);

Method signature:

bool HandleTickerDelegate(float DeltaSeconds,
TArray<FStar>& Stars,
TSubclassOf<ASLStarActor> BlueprintStar);

With the following errors:

error C2665: 'TDelegate<bool (float),FDefaultDelegateUserPolicy>::CreateUObject': no overloaded function could convert all the argument types
1> FTickerDelegate::CreateUObject(
1> ^
1>C:\Program Files\Epic Games\UE_5.8\Engine\Source\Runtime\Core\Public\Delegates\DelegateSignatureImpl.inl(586,79): note: could be 'TDelegate<bool (float),FDefaultDelegateUserPolicy> TDelegate<bool (float),FDefaultDelegateUserPolicy>::CreateUObject<USMyGameInstanceSubsys,TArray<FStar,FDefaultAllocator>&,TSubclassOf&>(const UserClass ,bool (__cdecl USMyGameInstanceSubsys:: )(float,TArray<FStar,FDefaultAllocator>,TSubclassOf) const,TArray<FStar,FDefaultAllocator> &,TSubclassOf &)'
1> with
1> [
1> UserClass=USMyGameInstanceSubsys
1> ]
1> [[nodiscard]] inline static TDelegate<RetValType(ParamTypes...), UserPolicy> CreateUObject(const UserClass* InUserObject, typename TMemFunPtrType<true, UserClass, RetValType (ParamTypes..., std::decay_t...)>::Type InFunc, VarTypes&&... Vars)
1> ^
1>C:\Source\MyGame\Source\MyModule\Private\SDMyGameInstanceSubsys.cpp(178,21): note: 'TDelegate<bool (float),FDefaultDelegateUserPolicy> TDelegate<bool (float),FDefaultDelegateUserPolicy>::CreateUObject<USMyGameInstanceSubsys,TArray<FStar,FDefaultAllocator>&,TSubclassOf&>(const UserClass ,bool (__cdecl USMyGameInstanceSubsys:: )(float,TArray<FStar,FDefaultAllocator>,TSubclassOf) const,TArray<FStar,FDefaultAllocator> &,TSubclassOf &)': cannot convert argument 2 from 'bool (__cdecl USMyGameInstanceSubsys::* )(float,TArray<FStar,FDefaultAllocator> &,TSubclassOf)' to 'bool (__cdecl USMyGameInstanceSubsys::* )(float,TArray<FStar,FDefaultAllocator>,TSubclassOf) const'
1> with
1> [
1> UserClass=USMyGameInstanceSubsys
1> ]
1> FTickerDelegate::CreateUObject(
1> ^
1>C:\Source\MyGame\Source\MyModule\Private\SDMyGameInstanceSubsys.cpp(180,5): note: Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or parenthesized function-style cast
1> &USMyGameInstanceSubsys::HandleTickerDelegate,
1> ^
1>C:\Program Files\Epic Games\UE_5.8\Engine\Source\Runtime\Core\Public\Delegates\DelegateSignatureImpl.inl(577,79): note: or 'TDelegate<bool (float),FDefaultDelegateUserPolicy> TDelegate<bool (float),FDefaultDelegateUserPolicy>::CreateUObject<USMyGameInstanceSubsys,TArray<FStar,FDefaultAllocator>&,TSubclassOf&>(UserClass ,bool (__cdecl USMyGameInstanceSubsys:: )(float,TArray<FStar,FDefaultAllocator>,TSubclassOf),TArray<FStar,FDefaultAllocator> &,TSubclassOf &)'
1> with
1> [
1> UserClass=USMyGameInstanceSubsys
1> ]
1> [[nodiscard]] inline static TDelegate<RetValType(ParamTypes...), UserPolicy> CreateUObject(UserClass* InUserObject, typename TMemFunPtrType<false, UserClass, RetValType (ParamTypes..., std::decay_t...)>::Type InFunc, VarTypes&&... Vars)
1> ^
1>C:\Source\MyGame\Source\MyModule\Private\SDMyGameInstanceSubsys.cpp(178,21): note: 'TDelegate<bool (float),FDefaultDelegateUserPolicy> TDelegate<bool (float),FDefaultDelegateUserPolicy>::CreateUObject<USMyGameInstanceSubsys,TArray<FStar,FDefaultAllocator>&,TSubclassOf&>(UserClass ,bool (__cdecl USMyGameInstanceSubsys:: )(float,TArray<FStar,FDefaultAllocator>,TSubclassOf),TArray<FStar,FDefaultAllocator> &,TSubclassOf &)': cannot convert argument 2 from 'bool (__cdecl USMyGameInstanceSubsys::* )(float,TArray<FStar,FDefaultAllocator> &,TSubclassOf)' to 'bool (__cdecl USMyGameInstanceSubsys::* )(float,TArray<FStar,FDefaultAllocator>,TSubclassOf)'
1> with
1> [
1> UserClass=USMyGameInstanceSubsys
1> ]
1> FTickerDelegate::CreateUObject(
1> ^
1>C:\Source\MyGame\Source\MyModule\Private\SDMyGameInstanceSubsys.cpp(180,5): note: Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or parenthesized function-style cast
1> &USMyGameInstanceSubsys::HandleTickerDelegate,
1> ^
1>C:\Program Files\Epic Games\UE_5.8\Engine\Source\Runtime\Core\Public\Delegates\DelegateSignatureImpl.inl(602,79): note: or 'TDelegate<bool (float),FDefaultDelegateUserPolicy> TDelegate<bool (float),FDefaultDelegateUserPolicy>::CreateUObject(TObjectPtr,TMemFunPtrType<true,UserClass,bool(float,decay::type...)>::Type,VarTypes ...)'
1> [[nodiscard]] inline static TDelegate<RetValType(ParamTypes...), UserPolicy> CreateUObject(TObjectPtr InUserObject, typename TMemFunPtrType<true, UserClass, RetValType (ParamTypes..., std::decay_t...)>::Type InFunc, VarTypes&&... Vars)
1> ^
1>C:\Source\MyGame\Source\MyModule\Private\SDMyGameInstanceSubsys.cpp(178,21): note: 'TDelegate<bool (float),FDefaultDelegateUserPolicy> TDelegate<bool (float),FDefaultDelegateUserPolicy>::CreateUObject(TObjectPtr,TMemFunPtrType<true,UserClass,bool(float,decay::type...)>::Type,VarTypes ...)': could not deduce template argument for 'TObjectPtr' from 'USMyGameInstanceSubsys *'
1> FTickerDelegate::CreateUObject(
1> ^
1>C:\Program Files\Epic Games\UE_5.8\Engine\Source\Runtime\Core\Public\Delegates\DelegateSignatureImpl.inl(593,79): note: or 'TDelegate<bool (float),FDefaultDelegateUserPolicy> TDelegate<bool (float),FDefaultDelegateUserPolicy>::CreateUObject(TObjectPtr,TMemFunPtrType<false,UserClass,bool(float,decay::type...)>::Type,VarTypes ...)'
1> [[nodiscard]] inline static TDelegate<RetValType(ParamTypes...), UserPolicy> CreateUObject(TObjectPtr InUserObject, typename TMemFunPtrType<false, UserClass, RetValType (ParamTypes..., std::decay_t...)>::Type InFunc, VarTypes&&... Vars)
1> ^
1>C:\Source\MyGame\Source\MyModule\Private\SDMyGameInstanceSubsys.cpp(178,21): note: 'TDelegate<bool (float),FDefaultDelegateUserPolicy> TDelegate<bool (float),FDefaultDelegateUserPolicy>::CreateUObject(TObjectPtr,TMemFunPtrType<false,UserClass,bool(float,decay::type...)>::Type,VarTypes ...)': could not deduce template argument for 'TObjectPtr' from 'USMyGameInstanceSubsys *'
1> FTickerDelegate::CreateUObject(
1> ^
1>C:\Source\MyGame\Source\MyModule\Private\SDMyGameInstanceSubsys.cpp(178,21): note: while trying to match the argument list '(USMyGameInstanceSubsys , bool (__cdecl USMyGameInstanceSubsys:: )(float,TArray<FStar,FDefaultAllocator> &,TSubclassOf), TArray<FStar,FDefaultAllocator>, TSubclassOf)'
1>C:\Source\MyGame\Source\MyModule\Private\SDMyGameInstanceSubsys.cpp(535,17): error C4458: declaration of 'AllStars' hides class member
1> TArray& AllStars,
1> ^
1>C:\Source\MyGame\Source\MyModule\Private\SDMyGameInstanceSubsys.h(110,16): note: see declaration of 'USMyGameInstanceSubsys::AllStars'
1> TArray AllStars;

The question is whether I need to add those two additional parameters to the method that’s going to use them. I suppose so, but I’m asking because I don’t understand the error.

/**
 * Ticker delegates return true to automatically reschedule at the same delay or false for one-shot.
 * You will not get more than one fire per "frame", which is just a FTSTicker::Tick call.
 * DeltaTime argument is the time since the last game frame, *not* since the last tick the
 * delegate received.
 */
DECLARE_DELEGATE_RetVal_OneParam(bool, FTickerDelegate, float);

As far as I can see, the expected delegate of the ticker takes a float as an argument and a bool as a return value. Since the ticker function is the caller of that delegate, even if you manage to add additional parameters to that delegate call, the ticker structure still cannot send those values to you. Therefore, if your desire is to access these values from inside the function rather than passing them as actual parameters, here are a few ways to work around that problem:

1. Using Lambda Expressions

You can use a lambda expression to capture the values you want to access if those values will no longer be reachable after they go out of scope. Alternatively, you can capture the this pointer to reach all the members of that class inside the lambda function. You can either run the logic directly inside the lambda function or, if you want to keep the lambda function clean, you can simply call “YourFunction” from within it.

2. Using Member Variables

If you don’t want to use lambdas, you can turn the parameters you want to pass into member variables. This way, instead of accessing them via function parameters, you can directly access those values because your function is still a member function and has access to them.

3. Creating a Temporary Structure

If you don’t want to mess with your actual class, you can create a temporary structure that contains those parameters as its own member variables. Instead of binding the function of your class, you can create an instance of that struct, set its member variables, and bind the function of the struct to the ticker delegate.

`TArray SomeValues;
TSubclassOf SomeClass;

FTickerDelegate TickerDelegateHandler = FTickerDelegate::CreateLambda([&SomeValues, SomeClass](float DeltaTime)
{
// Do something
});

//------------------//
FTickerDelegate TickerDelegateHandlerV2 = FTickerDelegate::CreateLambda([&SomeValues, SomeClass](float DeltaTime)
{
HandleTickerDelegate(DeltaTime, SomeValues, SomeClass);
});`

FTSTicker::FDelegateHandle TickDelegateHandle = FTSTicker::GetCoreTicker().AddTicker(TickerDelegateHandler);

I hope this is the answer to your question.

This is not accurate. A static delegate is capable of passing arbitrary parameters after the core parameters defined in the delegate signature.

If you look closely at the error message, you can see that the real reason what they were attempting failed to compile is that the actual function takes a reference parameter – it states it can’t convert a function taking the array by reference to a function taking the array by value.

With this method signature:

bool HandleTickerDelegate(float DeltaSeconds, TArray<FStar>& Stars, TSubclassOf<ASLStarActor> BlueprintStar);

And this delegate definition:

DECLARE_DELEGATE_RetVal_OneParam(bool, FTickerDelegate, float);

You should be able to do this:

FTickerDelegate TickerDelegateHandler =
     FTickerDelegate::CreateUObject(
          this,
          &USMyGameInstanceSubsys::HandleTickerDelegate,
          std::ref(Stars),
          BlueprintStar);

Note the difference on the second-to-last line.

For std::ref you need to #include <functional>, which is part of the standard C++ library. Unreal has equivalents to many of the things in the standard C++ library, but I haven’t been able to find this one.

If for some reason you can’t use the standard C++ library, you might be able to make it work with the following trivial definition of std::ref, though this is completely untested so don’t count on it.

template<typename T>
T& ref(T& r) { return r; }

Your other option would be to change the method signature to remove the & on the array parameter. It would be inefficient if you expect the array to have hundreds of elements or if you’re doing this on tick, but otherwise it shouldn’t really make a difference.

It really comes down to the TArray<FStar> &Stars. Because of the way those extra bindings work, they almost never any sort of reference parameter. If you change it to TArray<FStar> Stars it works just fine (or at least it did for me with FVector).

If you want to actually pass something through by reference, you’ll probably need a lambda delegate instead of a UObject one. I tried the std::ref suggestion from @SolarflareSoft , but it didn’t work for me (still compile errors on CreateUObject not on the call to std::ref.

However it’s hard to give great advice since we don’t see where Stars is coming from or why you’re passing by reference. If this is a member variable, you could just use it from the this of the UObject call. If it’s a local variable, your really, really don’t want to pass/capture it by reference or else the delegate will have bad data for that param when the delegate is called since that variable has fallen out of scope long ago.

TArray<FStar> Stars;is a local variable defined in the same method where the FTickerDelegate is defined:

TArray<FStar> Stars;

RetrieveStars(..., Stars);
// Signature:
// bool USMyGameInstanceSubsys::RetrieveStars(
//	   ...,
//	   TArray<FStar>& OutStars) const

FTickerDelegate TickerDelegateHandler =
     FTickerDelegate::CreateUObject(
          this,
          &USMyGameInstanceSubsys::HandleTickerDelegate,
          Stars,
          BlueprintStar);

FTSTicker::FDelegateHandle TickDelegateHandle = 
     FTSTicker::GetCoreTicker().AddTicker(TickerDelegateHandler);

All the above code is in one method.

Hope it helps!

Yeah, then like I said, you really, really don’t want to be passing that around by reference. The parameter passed to the delegate will be out of scope.

This is probably why the template argument code decays away references to prevent this sort of foot gun.

Semi-crazy alternative, if you’re really, really concerned about copying that array: you could create a UObject with a TArray<FStar> member and pass that to a delegate with a TStrongObjectPtr parameter. Then the only thing that is copied is the strong pointer, and as long as the delegate is around, the strong pointer will keep the UObject from being garbage collected.

As I mentioned in my comment, even if you manage to add new parameters to your delegate, the Ticker won’t pass those parameters to you. And as I mentioned, if they are temporary variables that are no longer reachable once they go out of scope, you should use a lambda—or the other solutions in that comment. However, my recommendation is to use a lambda.

Yes it will. Non-dynamic delegates use varidic templates to allow additional data binding from the point of delegate creation to be passed in in addition to whatever parameters are required by the delegate. The Ticker is not responsible for those parameters, the delegate that is created is and will pass them through when the delegate is executed.

The lambda would still need to capture by value and make a copy for use when executed from the delegate. But that’s the same thing that will happen if you change the function parameter from a reference to a value.

Indeed, this is an issue to be worried about. The std::ref style approach[1] is valid if you know the lifetime of the data being passed encloses the lifetime of the delegate, but in this case that delegate outlives the function and the data is a local variable, so it won’t work.

So the simplest option is to just pass the array by value, causing it to be copied into the delegate and copied again when the delegate is called. This should be fine if you only have a hundred or so elements (unless FStar itself contains huge arrays). If you’re expecting tens of thousands of elements or more, adding a level of indirection is probably better (keep reading for an example).

This is one possible way to add a level of indirection – change the function argument to TStrongObjectPtr<UStarList> or so. But it’s a bit excessive, as it requires you to create a class just for this purpose. My suggestion would be to use TSharedRef<TArray<FStar>> for this. (The first line may be incorrect; I don’t quite remember how MakeShareable works.)

TSharedRef<TArray<FStar>> Stars = MakeShareable<TArray<FStar>>();

RetrieveStars(..., *Stars);
// Signature:
// bool USMyGameInstanceSubsys::RetrieveStars(
//	   ...,
//	   TArray<FStar>& OutStars) const

FTickerDelegate TickerDelegateHandler =
     FTickerDelegate::CreateUObject(
          this,
          &USMyGameInstanceSubsys::HandleTickerDelegate,
          Stars,
          BlueprintStar);

FTSTicker::FDelegateHandle TickDelegateHandle = 
     FTSTicker::GetCoreTicker().AddTicker(TickerDelegateHandler);

The method signature would then be:

bool HandleTickerDelegate(float DeltaSeconds, TSharedRef<TArray<FStar>>& Stars, TSubclassOf<ASLStarActor> BlueprintStar);

  1. I’m aware by now that std::ref was noted to not compile, but it would probably work if the function parameter were to be changed to std::reference_wrapper<TArray<FStar>>. ↩︎

Oh, yeah a Shared Ref is a way better option. I need to get better about thinking of those things. They aren’t tools I usually reach for. Usually I only deal with them if they’re what some other function returns or wants.

Well, like I said, at least for this case it doesn’t even do that because it still doesn’t compile because the delegate won’t accept a reference parameter that way either. Or at least it didn’t compile for me when I tried it. The compiler was happy to use std::ref, but not to compile the delegate.

At least in this specific case of the a non-dynamic delegate and varidic template parameter.

I understand that, but if you read the footnote, I was saying it probably would’ve worked if the function were declared to take an std::reference_wrapper, which is what std::ref returns. The standard C++ function binding mechanics are more forgiving than Unreal’s are, I think, but if you make it match exactly then I would expect it to work. There’s also a chance that my trivial std::ref stand-in implementation would’ve worked.

Yup, I totally missed that. My bad. Not used to seeing those here, or on the rare occasion they’re just reference links and not actually footnotes! I wish it would actually put them in as foot notes instead of requiring a hover to even see (but that’s not your fault/problem).