TArray Resize bug? TArray not call Copy Constructor

At first, when I used TMap<FString, std::function <void(void)>>, I found that when inserting the fifth value (TMap is based on TArray, and the default initialization is 4), the previously inserted value will not valid.

After debugging, I was found that TArray did not call the Copy Constructor or Assignment Operator when ResizeAllocation (expanding memory).

To verify the problem, I build a structure to reproduce the problem.

代码如下:
The code is as follows:

struct MyStruct
{
    int Name;
    int* NameP;

    MyStruct()
        : Name(0), NameP(&Name)
    {

    }

    MyStruct(const int& InName)
    : Name(InName)
    , NameP(&Name)
    {
    }

    MyStruct(const MyStruct& Oth)
    : Name(Oth.Name)
    , NameP(&Name)
    {
    }

    MyStruct& operator = (const MyStruct& Oth)
    {
        Name = Oth.Name;
    }
};

TArray<MyStruct> Structs;
Structs.Emplace(111);
Structs.Emplace(222);
Structs.Emplace(333);
Structs.Emplace(444);

/// after this line, memory reallocate,
/// previously items's NameP pointer to a bad memory address
Structs.Emplace(555); 

The MyStruct has a int* member NameP, pointer to the member Name
When TArray need ‘ResizeAllocation’ (add, insert, resize or other operators),
TArray ResizeAllocation memory and not call OLD items’s Copy Constructor or Assignment Operators, the OLD items’s NameP is old and not valid.
TSet and TMap based TArray, the same problem exists!

Can someone tell me how to solve this problem?

I tried your example but didn’t get the issue, this is what I did

TArray<MyStruct> Structs;
	int32 index;
	index = Structs.Emplace(111);
	if(Structs[index].NameP != &Structs[index].Name){ UE_LOG(LogTemp, Warning, TEXT("Not the same!")); }
	index = Structs.Emplace(222);
	if (Structs[index].NameP != &Structs[index].Name) { UE_LOG(LogTemp, Warning, TEXT("Not the same!")); }
	index = Structs.Emplace(333);
	if (Structs[index].NameP != &Structs[index].Name) { UE_LOG(LogTemp, Warning, TEXT("Not the same!")); }
	index = Structs.Emplace(444);
	if (Structs[index].NameP != &Structs[index].Name) { UE_LOG(LogTemp, Warning, TEXT("Not the same!")); }
	index = Structs.Emplace(555);
	if (Structs[index].NameP != &Structs[index].Name) { UE_LOG(LogTemp, Warning, TEXT("Not the same!")); }

If you want to prevent reallocation and you know how many elements you will add you can reserve memory.
Structs.Reserve(5);

I might be wrong but I heard that assigning values like this does not guarantee the order would be the same.
MyStruct() : Name(0), NameP(&Name)

This is not a bug. This is the premise of TArray. TArray docs
A dynamically sized array of typed elements. Makes the assumption that your elements are relocate-able; i.e. that they can be transparently moved to new memory without a copy constructor.

(yes, some people still think switching to STL makes no sense)

1 Like

Thank.

The problem occurred at the 5th insertion.

TArray<MyStruct> Structs;
int32 index;
index = Structs.Emplace(111);
index = Structs.Emplace(222);
index = Structs.Emplace(333);
index = Structs.Emplace(444);

//  There is no problem with the above
index = Structs.Emplace(555);

if(Structs[0].NameP != &Structs[0].Name){ UE_LOG(LogTemp, Warning, TEXT("Not the same!")); }
if (Structs[1].NameP != &Structs[1].Name) { UE_LOG(LogTemp, Warning, TEXT("Not the same!")); }
if (Structs[2].NameP != &Structs[2].Name) { UE_LOG(LogTemp, Warning, TEXT("Not the same!")); }
if (Structs[3].NameP != &Structs[3].Name) { UE_LOG(LogTemp, Warning, TEXT("Not the same!")); }

// There is no problem with the 5th one
if (Structs[4].NameP != &Structs[4].Name) { UE_LOG(LogTemp, Warning, TEXT("Not the same!")); }

I know how to solve the problem.
I checked the source code of the UE and did not call the constructor.
I just want to confirm whether TArray is designed in this way.

1 Like

Thinks.
You solved my doubts. It seems that I didn’t read the document carefully.