lambda實現遞迴
在 C++ 中,lambda 表示式在定義時實際上不能直接呼叫自己,因為 lambda 在定義時沒有名字。要讓一個 lambda 自我引用,你需要使用一個技巧:將 lambda 自身作為引數傳遞給自己,從而實現遞迴。
為什麼 Lambda 自身在定義時無法被呼叫?
- 匿名性:Lambda 表示式是匿名的,編譯器在定義時不為其生成名稱,因此無法在其內部直接引用或呼叫自己。
- 捕獲和名稱:在 lambda 定義時,雖然可以捕獲外部變數,但不能直接引用自身,因為 lambda 的名字在定義時尚未確定。
解決方案:使用引用捕獲
透過使用 lambda 的引用捕獲(通常是將 lambda 自身作為引數傳遞),你可以在 lambda 內部實現遞迴。假設我們要查詢一個 AActor
下所有的 URectLightComponent
,我們可以使用一個 lambda 函式來遞迴地遍歷所有子元件。
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "Components/RectLightComponent.h"
#include "GameFramework/Actor.h"
class FRectLightFinder
{
public:
static void FindRectLightComponents(AActor* Actor, TArray<URectLightComponent*>& OutRectLightComponents)
{
if (!Actor)
{
return;
}
// 定義 lambda 函式進行遞迴
auto RecursiveSearch = [](USceneComponent* Component, TArray<URectLightComponent*>& OutComponents, auto& RecursiveSearchRef)
{
if (!Component)
{
return;
}
// 檢查是否是 RectLightComponent
if (URectLightComponent* RectLightComponent = Cast<URectLightComponent>(Component))
{
OutComponents.Add(RectLightComponent);
}
// 遞迴查詢所有子元件
for (USceneComponent* Child : Component->GetChildrenComponents(true))
{
RecursiveSearchRef(Child, OutComponents, RecursiveSearchRef);
}
};
// 從 Actor 的根元件開始遞迴
RecursiveSearch(Actor->GetRootComponent(), OutRectLightComponents, RecursiveSearch);
}
};
解釋
- Lambda 函式:
RecursiveSearch
是一個遞迴的 lambda 表示式,它負責遍歷元件及其子元件。注意,lambda 使用auto& RecursiveSearchRef
引數來遞迴呼叫自己。為了在 lambda 內部遞迴呼叫自身,必須透過引用捕獲。 RecursiveSearchRef
:這引數用於實現遞迴呼叫,因為 lambda 自身在定義時還無法被呼叫。我們將它作為引數傳遞給 lambda,並在遞迴時引用它。- 遞迴邏輯:在
RecursiveSearch
內部,我們首先檢查當前元件是否是URectLightComponent
。如果是,我們將其新增到結果陣列中。然後我們遞迴地遍歷當前元件的所有子元件。 - 啟動遞迴:在
FindRectLightComponents
函式中,我們從Actor
的根元件開始呼叫遞迴 lambda。
關鍵點
auto& RecursiveSearchRef
:在 lambda 的引數列表中,使用auto& RecursiveSearchRef
來傳遞 lambda 自身的引用。這允許 lambda 在其內部遞迴呼叫自身。- 遞迴呼叫:透過
RecursiveSearchRef
引用,lambda 可以遞迴呼叫自己,實現遍歷所有子元件的功能。
這種方式有效地繞過了 lambda 定義時無法直接自引用的問題,允許你在 lambda 內部實現遞迴邏輯。