I am asking for some advice on a problem that is hard to understand about the delegate of the Unreal Engine.

Trying to bind the function with parameter of pass by reference, and value type payload, makes compile error.

Is this illegal?

It is totally about my curiosity. I can work workaround this, by using a pointer type parameter. But, I want to understand this. I want to get knowledge of why this is happening. I searched for this for three days, but I could not get it.

This code makes a compile error.

This is header file.

//SomeEmptyPlugin.h
#pragma once

#include "CoreMinimal.h"
#include "Modules/ModuleManager.h"

DECLARE_DELEGATE(FSomeDelegate);

struct FSomeStruct
{
public:
	int32 SomeValue;

	FSomeStruct()
			: SomeValue(42)
	{}
	
	explicit FSomeStruct(int32 InSomeValue)
		: SomeValue(InSomeValue)
	{}
};

class FSomeEmptyPluginModule : public IModuleInterface
{
public:

	FSomeDelegate DelegateWithoutPayload;
	FSomeDelegate DelegateWithValuePayload;
	FSomeDelegate DelegateWithPointerPayload;
	FSomeDelegate DelegateWithReferencePayload;

	FSomeStruct SomeStruct;
	
	void SomeTest();

	void FunctionWithoutParameter();
	void FunctionWithValueParameter(FSomeStruct SomePayLoad);
	void FunctionWithPointerParameter(FSomeStruct* SomePayLoad);
	void FunctionWithReferenceParameter(FSomeStruct& SomePayLoad);
	
	/** IModuleInterface implementation */
	virtual void StartupModule() override;
	virtual void ShutdownModule() override;
};

And this is source file.

//SomeEmptyPlugin.cpp
#include "SomeEmptyPlugin.h"

#define LOCTEXT_NAMESPACE "FSomeEmptyPluginModule"

void FSomeEmptyPluginModule::SomeTest()
{
	DelegateWithoutPayload.BindRaw(
	this
	, &FSomeEmptyPluginModule::FunctionWithoutParameter
	);

	DelegateWithValuePayload.BindRaw(
		this
		, &FSomeEmptyPluginModule::FunctionWithValueParameter
		, SomeStruct
		);

	DelegateWithPointerPayload.BindRaw(
		this
		, &FSomeEmptyPluginModule::FunctionWithPointerParameter
		, &SomeStruct
		);
	
	DelegateWithReferencePayload.BindRaw(
		this
		, &FSomeEmptyPluginModule::FunctionWithReferenceParameter
		, SomeStruct
		); //This is making the compile error!
	
	void (FSomeEmptyPluginModule::*SomeFunctionPtrWithValueParam)(FSomeStruct) = nullptr;
	SomeFunctionPtrWithValueParam = &FSomeEmptyPluginModule::FunctionWithValueParameter;
	(this->*SomeFunctionPtrWithValueParam)(this->SomeStruct);

	void (FSomeEmptyPluginModule::*SomeFunctionPtrWithRefParam)(FSomeStruct&) = nullptr;
	SomeFunctionPtrWithRefParam = &FSomeEmptyPluginModule::FunctionWithReferenceParameter;
	(this->*SomeFunctionPtrWithRefParam)(this->SomeStruct);
	
}

void FSomeEmptyPluginModule::FunctionWithoutParameter()
{
	UE_LOG(LogTemp, Warning, TEXT("SomeFunction called"));
}

void FSomeEmptyPluginModule::FunctionWithValueParameter
(FSomeStruct SomePayLoad)
{
	UE_LOG(LogTemp, Warning, TEXT("SomeLog %d"), SomePayLoad.SomeValue);
}

void FSomeEmptyPluginModule::FunctionWithPointerParameter
(FSomeStruct* SomePayLoad)
{
	UE_LOG(LogTemp, Warning, TEXT("SomeLog %d"), SomePayLoad->SomeValue);
}

void FSomeEmptyPluginModule::FunctionWithReferenceParameter
(FSomeStruct& SomePayLoad)
{
	UE_LOG(LogTemp, Warning, TEXT("SomeLog %d"), SomePayLoad.SomeValue);
}

void FSomeEmptyPluginModule::StartupModule()
{
	SomeTest();
}

void FSomeEmptyPluginModule::ShutdownModule()
{	
}

#undef LOCTEXT_NAMESPACE
	
IMPLEMENT_MODULE(FSomeEmptyPluginModule, SomeEmptyPlugin)

Binding about these works well.

  • DelegateWithoutPayload and FunctionWithoutParameter
  • DelegateWithValuePayload and FunctionWithValueParameter
  • DelegateWithPointerPayload and FunctionWithPointerParameter

But, Binding about this, makes compile error.

  • DelegateWithReferencePayload and FunctionWithReferenceParameter

The compile error is like below:

In IDE, I can see this with tooltip.

No viable function. Argument types: FSomeEmptyPluginModule*, void(FSomeEmptyPluginModule::*)(FSomeStruct&), FSomeStruct. Candidates considered:   inline void BindRaw<UserClass, VarTypes...>(UserClass* InUserObject, typename TMemFunPtrType<false, UserClass, TDelegate<void()>::RetValType(std::decay_t<VarTypes>...)>::Type InFunc, VarTypes&&...) (in class TDelegate<void(), FDefaultDelegateUserPolicy>)     conversion of 2nd argument &FSomeEmptyPluginModule::FunctionWithReferenceParameter is ill-formed: cannot convert void(FSomeEmptyPluginModule::*)(FSomeStruct&) to parameter type TMemFunPtrType<false, FSomeEmptyPluginModule, TDelegate<void()>::RetValType(std::decay_t<FSomeStruct&>)>::Type (aka void(FSomeEmptyPluginModule::*)(FSomeStruct)),       because source function and target function have different 1st parameter types: FSomeStruct& and FSomeStruct   inline void BindRaw<UserClass, VarTypes...>(const UserClass* InUserObject, typename TMemFunPtrType<true, UserClass, TDelegate<void()>::RetValType(std::decay_t<VarTypes>...)>::Type InFunc, VarTypes&&...) (in class TDelegate<void(), FDefaultDelegateUserPolicy>)     conversion of 2nd argument &FSomeEmptyPluginModule::FunctionWithReferenceParameter is ill-formed: cannot convert void(FSomeEmptyPluginModule::*)(FSomeStruct&) to parameter type TMemFunPtrType<true, FSomeEmptyPluginModule, TDelegate<void()>::RetValType(std::decay_t<FSomeStruct&>)>::Type (aka void(FSomeEmptyPluginModule::*)(FSomeStruct) const),       because target function has const qualifier

And I can see after trying to compile:

0>SomeEmptyPlugin.cpp(26): Error C2665 : 'TDelegate<void (void),FDefaultDelegateUserPolicy>::BindRaw': no overloaded function could convert all the argument types
0>DelegateSignatureImpl.inl(400): Reference  : could be 'void TDelegate<void (void),FDefaultDelegateUserPolicy>::BindRaw<FSomeEmptyPluginModule,FSomeStruct&>(const UserClass *,void (__cdecl FSomeEmptyPluginModule::* )(FSomeStruct) const,FSomeStruct &)'
0>        with
0>        [
0>            UserClass=FSomeEmptyPluginModule
0>        ]
0>SomeEmptyPlugin.cpp(26): Reference  : 'void TDelegate<void (void),FDefaultDelegateUserPolicy>::BindRaw<FSomeEmptyPluginModule,FSomeStruct&>(const UserClass *,void (__cdecl FSomeEmptyPluginModule::* )(FSomeStruct) const,FSomeStruct &)': cannot convert argument 2 from 'void (__cdecl FSomeEmptyPluginModule::* )(FSomeStruct &)' to 'void (__cdecl FSomeEmptyPluginModule::* )(FSomeStruct) const'
0>        with
0>        [
0>            UserClass=FSomeEmptyPluginModule
0>        ]
0>SomeEmptyPlugin.cpp(28): Reference  : Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or parenthesized function-style cast
0>DelegateSignatureImpl.inl(393): Reference  : or       'void TDelegate<void (void),FDefaultDelegateUserPolicy>::BindRaw<FSomeEmptyPluginModule,FSomeStruct&>(UserClass *,void (__cdecl FSomeEmptyPluginModule::* )(FSomeStruct),FSomeStruct &)'
0>        with
0>        [
0>            UserClass=FSomeEmptyPluginModule
0>        ]
0>SomeEmptyPlugin.cpp(26): Reference  : 'void TDelegate<void (void),FDefaultDelegateUserPolicy>::BindRaw<FSomeEmptyPluginModule,FSomeStruct&>(UserClass *,void (__cdecl FSomeEmptyPluginModule::* )(FSomeStruct),FSomeStruct &)': cannot convert argument 2 from 'void (__cdecl FSomeEmptyPluginModule::* )(FSomeStruct &)' to 'void (__cdecl FSomeEmptyPluginModule::* )(FSomeStruct)'
0>        with
0>        [
0>            UserClass=FSomeEmptyPluginModule
0>        ]
0>SomeEmptyPlugin.cpp(28): Reference  : Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or parenthesized function-style cast
0>SomeEmptyPlugin.cpp(26): Reference  : while trying to match the argument list '(FSomeEmptyPluginModule *, void (__cdecl FSomeEmptyPluginModule::* )(FSomeStruct &), FSomeStruct)'

According to my short knowledge of c++, a parameter of the reference type can get value type input. But the codes above does not.

I searched and read again about below in the last three days:

  • std::decay_t<>, But this is not the cause. I tried UE 5.1, which is without std::decay_t<>.
  • reference collapse
  • template type deduction
  • move semantics, perfect forwarding, universal references

But those seem not related to, in my seeing.

When this pattern of problems happened to me,
there was a fairly high probability that it was due to my lack of basic knowledge.

What is happening inside there?

Have you tried std::ref?

DelegateWithReferencePayload.BindRaw(
		this
		, &FSomeEmptyPluginModule::FunctionWithReferenceParameter
		, std::ref(SomeStruct)
		);
1 Like

Thank you for reply. But I did not have a luck.

#include "SomeEmptyPlugin.h"
#include<functional> 

#define LOCTEXT_NAMESPACE "FSomeEmptyPluginModule"

void FSomeEmptyPluginModule::SomeTest()
{
	DelegateWithoutPayload.BindRaw(
	this
	, &FSomeEmptyPluginModule::FunctionWithoutParameter
	);

	DelegateWithValuePayload.BindRaw(
		this
		, &FSomeEmptyPluginModule::FunctionWithValueParameter
		, SomeStruct
		);

	DelegateWithPointerPayload.BindRaw(
		this
		, &FSomeEmptyPluginModule::FunctionWithPointerParameter
		, &SomeStruct
		);
	
	DelegateWithReferencePayload.BindRaw(
		this
		, &FSomeEmptyPluginModule::FunctionWithReferenceParameter
		, std::ref(SomeStruct)
		);
	
	void (FSomeEmptyPluginModule::*SomeFunctionPtrWithValueParam)(FSomeStruct) = nullptr;
	SomeFunctionPtrWithValueParam = &FSomeEmptyPluginModule::FunctionWithValueParameter;
	(this->*SomeFunctionPtrWithValueParam)(this->SomeStruct);

	void (FSomeEmptyPluginModule::*SomeFunctionPtrWithRefParam)(FSomeStruct&) = nullptr;
	SomeFunctionPtrWithRefParam = &FSomeEmptyPluginModule::FunctionWithReferenceParameter;
	(this->*SomeFunctionPtrWithRefParam)(this->SomeStruct);
	
}

And I got this message. In IDE Toolip,

No viable function. Argument types: FSomeEmptyPluginModule*, void(FSomeEmptyPluginModule::*)(FSomeStruct&), std::reference_wrapper<FSomeStruct>. Candidates considered:   inline void BindRaw<UserClass, VarTypes...>(UserClass* InUserObject, typename TMemFunPtrType<false, UserClass, TDelegate<void()>::RetValType(std::decay_t<VarTypes>...)>::Type InFunc, VarTypes&&...) (in class TDelegate<void(), FDefaultDelegateUserPolicy>)     conversion of 2nd argument &FSomeEmptyPluginModule::FunctionWithReferenceParameter is ill-formed: cannot convert void(FSomeEmptyPluginModule::*)(FSomeStruct&) to parameter type TMemFunPtrType<false, FSomeEmptyPluginModule, TDelegate<void()>::RetValType(std::decay_t<std::reference_wrapper<FSomeStruct>>)>::Type (aka void(FSomeEmptyPluginModule::*)(std::reference_wrapper<FSomeStruct>)),       because source function and target function have different 1st parameter types: FSomeStruct& and std::reference_wrapper<FSomeStruct>   inline void BindRaw<UserClass, VarTypes...>(const UserClass* InUserObject, typename TMemFunPtrType<true, UserClass, TDelegate<void()>::RetValType(std::decay_t<VarTypes>...)>::Type InFunc, VarTypes&&...) (in class TDelegate<void(), FDefaultDelegateUserPolicy>)     conversion of 2nd argument &FSomeEmptyPluginModule::FunctionWithReferenceParameter is ill-formed: cannot convert void(FSomeEmptyPluginModule::*)(FSomeStruct&) to parameter type TMemFunPtrType<true, FSomeEmptyPluginModule, TDelegate<void()>::RetValType(std::decay_t<std::reference_wrapper<FSomeStruct>>)>::Type (aka void(FSomeEmptyPluginModule::*)(std::reference_wrapper<FSomeStruct>) const),       because target function has const qualifier

And in the compile error,

0>Executing up to 12 actions based on MaxProcessorCount override
0>------ Building 4 action(s) started ------
0>[1/4] Compile [x64] Module.SomeEmptyPlugin.cpp
0>SomeEmptyPlugin.cpp(27): Error C2665 : 'TDelegate<void (void),FDefaultDelegateUserPolicy>::BindRaw': no overloaded function could convert all the argument types
0>DelegateSignatureImpl.inl(400): Reference  : could be 'void TDelegate<void (void),FDefaultDelegateUserPolicy>::BindRaw<FSomeEmptyPluginModule,std::reference_wrapper<FSomeStruct>>(const UserClass *,void (__cdecl FSomeEmptyPluginModule::* )(std::reference_wrapper<FSomeStruct>) const,std::reference_wrapper<FSomeStruct> &&)'
0>        with
0>        [
0>            UserClass=FSomeEmptyPluginModule
0>        ]
0>SomeEmptyPlugin.cpp(27): Reference  : 'void TDelegate<void (void),FDefaultDelegateUserPolicy>::BindRaw<FSomeEmptyPluginModule,std::reference_wrapper<FSomeStruct>>(const UserClass *,void (__cdecl FSomeEmptyPluginModule::* )(std::reference_wrapper<FSomeStruct>) const,std::reference_wrapper<FSomeStruct> &&)': cannot convert argument 2 from 'void (__cdecl FSomeEmptyPluginModule::* )(FSomeStruct &)' to 'void (__cdecl FSomeEmptyPluginModule::* )(std::reference_wrapper<FSomeStruct>) const'
0>        with
0>        [
0>            UserClass=FSomeEmptyPluginModule
0>        ]
0>SomeEmptyPlugin.cpp(29): Reference  : Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or parenthesized function-style cast
0>DelegateSignatureImpl.inl(393): Reference  : or       'void TDelegate<void (void),FDefaultDelegateUserPolicy>::BindRaw<FSomeEmptyPluginModule,std::reference_wrapper<FSomeStruct>>(UserClass *,void (__cdecl FSomeEmptyPluginModule::* )(std::reference_wrapper<FSomeStruct>),std::reference_wrapper<FSomeStruct> &&)'
0>        with
0>        [
0>            UserClass=FSomeEmptyPluginModule
0>        ]
0>SomeEmptyPlugin.cpp(27): Reference  : 'void TDelegate<void (void),FDefaultDelegateUserPolicy>::BindRaw<FSomeEmptyPluginModule,std::reference_wrapper<FSomeStruct>>(UserClass *,void (__cdecl FSomeEmptyPluginModule::* )(std::reference_wrapper<FSomeStruct>),std::reference_wrapper<FSomeStruct> &&)': cannot convert argument 2 from 'void (__cdecl FSomeEmptyPluginModule::* )(FSomeStruct &)' to 'void (__cdecl FSomeEmptyPluginModule::* )(std::reference_wrapper<FSomeStruct>)'
0>        with
0>        [
0>            UserClass=FSomeEmptyPluginModule
0>        ]
0>SomeEmptyPlugin.cpp(29): Reference  : Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or parenthesized function-style cast
0>SomeEmptyPlugin.cpp(27): Reference  : while trying to match the argument list '(FSomeEmptyPluginModule *, void (__cdecl FSomeEmptyPluginModule::* )(FSomeStruct &), std::reference_wrapper<FSomeStruct>)'

Yes, there are limitations to what you can pre-bind as a delegate parameter for use later when it’s invoked. The underlying structure of the delegate (probably) requires all the data to be default constructible and assignable, two things you can’t do with a reference member.

In a way this also protects against naive implementations that would easily cause crashes by passing references to local variables who’s memory is no longer valid when the delegate is invoked. You can cause the same problem with a pointer type, but at least then you’re having to be explicit in a way that (hopefully) makes you think about the value that you’re binding to the delegate.

1 Like

Thank you so much! I really wanted to know about this clearly.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.