TMap custom KeyFuncs fails to compile

Hello there,

I stumbled on a compilation error when trying to make and use a custom KeyFuncs struct for a TMap, in the case the type of the key of the map and the “InKeyType” are different and not implicitly convertible to each other (see below snippet for clearer explanation)

template <typename MapValueType>
struct TMyStructMapKeyFuncs : BaseKeyFuncs<TPair<FMyStruct, MapValueType>, FString, false>
//                                        This type ^^^                      ^^^ and this type are different  
{
//...
}

The compilation error happens when calling the TMap::Contains method, but I believe other functions like RemoveAndCopyValue and Find (and maybe others) suffer from the same problem. When we look at the TMap::Contains implementation, the Key parameter is a KeyConstPointerType (in our case, a const FMyStruct&), and it forwards it directly to its internal Pairs.Contains (which is essentially a TSet<TPair<MapKeyType, MapValueType>, MapKeyFuncs>). Problem is that TSet::Contains expects a KeyInitType, extracted from the KeyFuncs struct, which is a FString in our case. There’s no possible conversion which triggers the compilation error.

I think that TMap::Contains should look like something like this instead

[[nodiscard]] FORCEINLINE bool Contains(KeyConstPointerType Key) const
{
	return Pairs.Contains(KeyFuncs::GetSetKey(Key));
}

Is this an implementation miss/error or the expected usage is different? Thank you in advance for the feedback!

Steps to Reproduce
Reproduced in launcher UE 5.7.3 vanilla.

  1. Copy-paste the MyCustomKeyFuncs.cpp snippet from the official TMap documentation (Map Containers in Unreal Engine | Unreal Engine 5.7 Documentation | Epic Developer Community)
  2. Declare a TMap using the custom KeyFuncs and try to call Contains on it.
  3. Notice the compilation error.

Full snippet

struct FMyStruct
{
	// String which identifies our key
	FString UniqueID;
};


template <typename ValueType>
struct TMyStructMapKeyFuncs : BaseKeyFuncs<TPair<FMyStruct, ValueType>, FString>
{
private:
	typedef BaseKeyFuncs<TPair<FMyStruct, ValueType>, FString> Super;

public:
	typedef typename Super::ElementInitType ElementInitType;
	typedef typename Super::KeyInitType     KeyInitType;


	static KeyInitType GetSetKey(ElementInitType Element)
	{
		return Element.Key.UniqueID;
	}

	static bool Matches(KeyInitType A, KeyInitType B)
	{
		return A.Compare(B, ESearchCase::CaseSensitive) == 0;
	}

	static uint32 GetKeyHash(KeyInitType Key)
	{
		return FCrc::StrCrc32(*Key);
	}
};


struct FTriggerCompilationError
{
	TMap<FMyStruct, int32, FDefaultSetAllocator, TMyStructMapKeyFuncs<int32>> Map;

	FTriggerCompilationError()
	{
		FMyStruct MyStruct;
		if (Map.Contains(MyStruct)) {}
	}
};

Just for reference, as a workaround it works to write the TMap KeyFuncs like this instead. Is it the expected usage and the documentation is out-of-date, or the initial plan was to handle the type conversion in the TMap implementation?

template <typename ValueType>
struct TMyStructMapKeyFuncs : BaseKeyFuncs<TPair<FMyStruct, ValueType>, FMyStruct>
{
private:
	typedef BaseKeyFuncs<TPair<FMyStruct, ValueType>, FMyStruct> Super;
 
public:
	typedef typename Super::ElementInitType ElementInitType;
	typedef typename Super::KeyInitType     KeyInitType;
 
 
	static KeyInitType GetSetKey(ElementInitType Element)
	{
		return Element.Key;
	}
 
	static bool Matches(KeyInitType A, KeyInitType B)
	{
		return A.UniqueID.Compare(B.UniqueID, ESearchCase::CaseSensitive) == 0;
	}
 
	static uint32 GetKeyHash(KeyInitType Key)
	{
		return FCrc::StrCrc32(*Key.UniqueID);
	}
};

Hey Julien,

thanks for the report, you’re right that the docs example currently doesn’t work.

I’ve reached out to the experts on our containers to find out what the intended usage here was.

We have lot’s of examples of custom keyfuncs in the code but I couldn’t find a single one that uses the pattern described in that docs page.

I’ll let you know once I’ve heard back from the team.

Best,

Sebastian

Alright,

we’ve had a look and there’s indeed a few cases where the KeyConstPointerType was incorrectly used.

A fix was just submitted as CL51837460 in UE5/Main (18cb0d28 on Github), so this should hopefully fix those type of issues.

Thanks again for letting us know!

Cheers,

Sebastian

Thank you very much for the reactive answer and fix.

I haven’t had a chance to test it yet but will cherry pick and give it a try when I have a bit of free time.

Thanks again!

Hi - Is there anything else you’d need support for on this topic at this time? If not, I’ll close this ticket. Thanks!

The ticket can be closed! Thank you for the support :slight_smile: