Unreal Engine TArray Chained call giving different result compares to not using one

Problem

I want to filter an array as shown below:

  1. Temporarily save the result of allVertexGridNodes and then filter:

    auto gridNodes = allVertexGridNodes();
    auto filteredResult = gridNodes.FilterByPredicate([]() {});
    
  2. Chain using allVertexGridNodes and filter:

    auto filteredResult = allVertexGridNodes().FilterByPredicate([]() {});
    

These 2 cases give me different results. Is this by design?

Expected Result

Case 1 should be equal to case 2 in all aspects.

Phenomenon in Detail

  • In case 1, filteredResult is an array as expected.
  • In case 2, filteredResult is also an array with the same number of elements as case 1, but there is always one invalid element in this array. The values in this invalid element are unpredictable, like a pointer with all its properties released.

Environment

Unreal Engine 5.4

Things Tried

  1. In Unreal Engine 5.4’s source code, it’s 100% reproducible on my PC.
  2. I’ve tried some simple cases in a C+±only context without using UE in VS Code, using only std::vector and std::shared_ptr, but failed to reproduce this issue.
  3. Using a plain old for loop in the code below gives the same result, so it’s not an issue with Algo.

Code

TArray<TSharedPtr<PositionableNode>> allVertexGridNodes() const
{
    TArray<TSharedPtr<PositionableNode>> vertexNodes{};
    Algo::TransformIf(
        m_nodes, vertexNodes, 
        [](const TSharedPtr<Node>& node) { return node && !node->isEdge; },
        [](const TSharedPtr<Node>& node) { return StaticCastSharedPtr<PositionableNode>(node); });

    return vertexNodes;
}

Some Thoughts

  1. I should copy all the TArray related code to C++ to try to reproduce this in VS Code, but I’m overwhelmed by C++. The memory management of TArray seems incomprehensible to me as a junior C++ learner.
  2. I really want to use chaining in C++, just like in some other languages. (Not using chaining in this case seems verbose to me.)

hi ,
could you try reference assigment like this and check the results:

auto gridNodes { allVertexGridNodes()};
auto filteredResult { gridNodes.FilterByPredicate( {})};
auto filteredResult2 { allVertexGridNodes().FilterByPredicate( {}) };

Hi @tootzoe1 ! Thank you so much for you reply.

Your suggestion is accepted and tested.
Here is the result.(Only testing the last one,since case 1 is an OK for me)
Please feel free to ask for more , if you want more info or tests!

TLDR(joking)

Reference Assignment works.
But plain chaining without reference assignment keeps giving incredible result.

Test cases

case result note
VertexNodeShouldIncludeNickname61 OK using temp variable to save filtered result
VertexNodeShouldIncludeNickname61WithChaining NG Chaining without using reference assignment
VertexNodeShouldIncludeNickname61UsingRefAssign OK Chaining with reference assignment

Test code

            It("VertexNodeShouldIncludeNickname61",
                [this]()
                {
                    auto vertexes = grid->allVertexGridNodes();
                    auto Is61Found = vertexes.FindByPredicate(
                        [](const TSharedPtr<PositionableNode>& node) {
                            return node->nicknameByPosition == FIntPoint{6, 1} && node->belongedPlayer == PlayerType::Player1;
                        });  //

                    TestTrueExpr(Is61Found->IsValid());
                    TestTrueExpr(((*Is61Found)->nicknameByPosition == FIntPoint{6, 1}));
                });

            It("VertexNodeShouldIncludeNickname61WithChaining",
                [this]()
                {
                    auto Is61Found = grid->allVertexGridNodes().FindByPredicate(
                        [](const TSharedPtr<PositionableNode>& node) {
                            return node->nicknameByPosition == FIntPoint{6, 1} && node->belongedPlayer == PlayerType::Player1;
                        });  //

                    TestTrueExpr(Is61Found->IsValid());
                    TestTrueExpr(((*Is61Found)->nicknameByPosition == FIntPoint{6, 1}));
                });

            It("VertexNodeShouldIncludeNickname61UsingRefAssign",
                [this]()
                {
                    // testing reference assignment
                    auto Is61FoundByRefAssign{grid->allVertexGridNodes().FilterByPredicate(
                        [](const TSharedPtr<PositionableNode>& node) {
                            return node->nicknameByPosition == FIntPoint{6, 1} && node->belongedPlayer == PlayerType::Player1;
                        })};  //

                    if (TestTrueExpr(!Is61FoundByRefAssign.IsEmpty()))
                    {
                        TestTrueExpr(Is61FoundByRefAssign.Top().IsValid());
                        TestTrueExpr((Is61FoundByRefAssign.Top()->nicknameByPosition == FIntPoint{6, 1}));
                    }
                });

test result


(post deleted by author)