Why we cannot use Destroy and FTimerHandle() in the SetTimer method?

I tried not using the FTimerHandle Time variable and pass FTimerHandle() directly in the SetTimer method

GetWorld()->GetTimerManager().SetTimer(FTimerHandle(), this, &AProjectile::OnTimerExpire, DestroyDelay);

And I got this error

error C4239: nonstandard extension used: ‘argument’: conversion from ‘FTimerHandle’ to 'FTimerHandle &'

and

note: A non-const reference may only be bound to an lvalue

If I change this code to

FTimerHandle Timer = FTimerHandle();
GetWorld()->GetTimerManager().SetTimer(Timer, this, &AProjectile::OnTimerExpire, DestroyDelay);

or the Timer without FTimerHandle() constructor I get no compiler errors, but I cannot understand why this works.

I also tried using the Destroy method of AProjectile directly in the SetTimer method

GetWorld()->GetTimerManager().SetTimer(Timer, this, &AProjectile::Destroy, DestroyDelay);

But I got this error

error C2664: ‘void FTimerManager::SetTimer(FTimerHandle &,TFunction<void (void)> &&,float,bool,float)’: cannot convert argument 3 from ‘bool (__cdecl AActor::* )(bool,bool)’ to 'void (__cdecl AProjectile::* )(void)'

and

note: Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast

I just do not understand this too, isn’t Destroy AProjectile’s method too?

Are those errors C++ errors or just something that comes with Unreal Engine 4?

Thanks in advance.

P.S. I am new in C++, that’s why I am asking those questions, as I am trying to get a better understanding.

Interesting questions.

  1. I would assume something like this is happening.
  2. Destroy is an inherited member and not virtual so it would be AActor::Destroy and not AProjectile::Destroy

Though you should be able to use FTimerDelegate and use a lambda expression if you don’t want to create a function just to be used for a single delegate

FTimerDelegate Delegate;
Delegate.BindLambda([this] { Destroy(); });
//Uses different SetTimer overload
GetWorld()->GetTimerManager().SetTimer(Timer, Delegate, DestroyDelay, false);
1 Like

Thanks for the reply.
So are those just “protection mechanisms” from the side of the c++ compiler?
I mean FTimerHandle will be removed imidiatly after the call of SetTimer as it is going to get out of scope, so the compiler just checked if the object existed outside the function (SetTimer).

And in the case of the method Destroy just again the compiler in this particular case acknowledges only the methods of the class and not of the super class.

I don’t actually know where in the code that error happens so can’t say with 100% certainty.

Because it’s a function template that looks like this

template< class UserClass >
FORCEINLINE void SetTimer(FTimerHandle& InOutHandle, UserClass* InObj, typename FTimerDelegate::TUObjectMethodDelegate_Const< UserClass >::FMethodPtr InTimerMethod, float InRate, bool InbLoop = false, float InFirstDelay = -1.f)

So if you look at the second and third parameters, the second is what you used this with. This would mean that UserClass is whatever type this is; In our case AProjectile which would then be used in the third parameter which would be some form of a function pointer of the same type (AProjectile).

However since Destroy isn’t a virtual function the type AProjectile::Destroy is actually AActor::Destroy meaning the compiler cannot generate that template since the argument types for the second and third parameters (AProjectile & AActor) don’t match and also can’t be implicitly converted from one to the other, and the template says they should be the same type so compilation error.

2 Likes

Thanks, again.

I want to get to the bottom of why OnTimerExpire isn’t working and how to get around it but FTimerDelegate and the SetTimer overload you used seems like just less of a headache.

Privacy & Terms