代码之家  ›  专栏  ›  技术社区  ›  Jimmyt1988

无法计算C++λ表达式的语法作为方法的参数

  •  -2
  • Jimmyt1988  · 技术社区  · 5 年前

    问题:

    如何将lambda表达式传递给函数。例如:

    int GetCoolInt()
    {
        int a = 3;
    
        return AddToInt( [a] (int b) {
            a += b;
        });    
    }
    
    int AddToInt(int func(int output))
    {
        return func(3);
    }
    
    int output = GetCoolInt();
    

    我的问题背景:

    我有以下两种方法:

    FindPlayerStartByTag 电话 FindActor 使用将在其他方法中使用的逻辑。

    //template <typename T>
    APlayerStart* FindPlayerStartByTag(FName tag)
    {
        return FindActor<APlayerStart>(tag, [tag](TObjectIterator<APlayerStart> actor) {
            APlayerStart* playerStart;
            if (actor->PlayerStartTag == tag)
            {
                playerStart = *actor;
            }
            return playerStart;
        });
    }
    

    此方法是在迭代器的帮助下查找特定对象的逻辑。 查找图层开始字节标记 逻辑

    template <typename T>
    T* FindActor(FName tag, T* func(TObjectIterator<T> actor))
    {
        T* returnValue;
    
        if (!tag.IsNone())
        {
            // Search for actor
            for (TObjectIterator<T> itr; itr; ++itr)
            {
                if (itr->IsA(T::StaticClass()))
                {
                    returnValue = func(itr);
                    if (returnValue)
                    {
                        break;
                    }
                }
            }
        }
    
        return returnValue;
    }
    

    我当前有此错误:

    enter image description here

    考虑到第三种方法,我对它的含义感到困惑:

    template <typename T>
    T* FindActorByTag(FName tag)
    {
        return FindActor<T>(tag, [tag](TObjectIterator<AActor> actor)
        {
            T* foundActor;
            for (FName currentActorTag : actor->Tags)
            {
                if (tag == currentActorTag)
                {
                    foundActor = *actor;
                }
            }
            return foundActor;
        });
    }
    

    编译得很好。

    我可以通过添加模板看到这一点 <typename T> 消除错误(请参见 //template <typename T> 但是我不需要这样做,因为对于Aplayerstart,我已经知道方法需要是什么类型。

    有人能解释一下吗?

    谢谢!

    编辑:

    以下是重构到此处之前使用的方法的版本:

    APlayerStart* FindPlayerStartByTag(FName tag)
    {
        /*return FindActor<APlayerStart>(tag, [tag](TObjectIterator<APlayerStart> actor) {
            APlayerStart* playerStart;
            if (actor->PlayerStartTag == tag)
            {
                playerStart = *actor;
            }
            return playerStart;
        });*/
    
        APlayerStart* returnValue;
    
        if (!tag.IsNone())
        {
            // Search for actor
            for (TObjectIterator<APlayerStart> itr; itr; ++itr)
            {
                if (itr->IsA(APlayerStart::StaticClass()) && itr->PlayerStartTag == tag)
                {
                    returnValue = *itr;
                }
            }
        }
    
        return returnValue;
    }
    

    但编译。

    编辑2:

    这就是我打算如何使用这些方法:

    PlayerStart = ActorHelper->FindPlayerStartByTag(PlayerStartTag);
    ATreasureChestActor* treasureChestActor = ActorHelper->FindActorByTag<ATreasureChestActor>(TreasureActorTagName);
    

    编辑3:

    这个问题似乎来自于闭包的用法!

    这是使用一个闭包变量:

    enter image description here

    这是没有:

    enter image description here

    1 回复  |  直到 5 年前
        1
  •  2
  •   bolov    5 年前

    你的文章仍然一团糟,同一期有4个不同版本。我将关注第一个代码片段,因为它似乎是最接近[mcve]的代码片段,我将澄清如何正确使用lambda和函数对象。

    int AddToInt(int func(int output))
    

    这有点误导人。我建议将其更改为等效的,但更多地用于:

    int AddToInt(int (*func)(int))
    

    这意味着:声明一个名为 AddToInt 其中:

    • 接受“指向接受int并返回int的函数的指针”类型的参数,并且
    • 返回一个 int .

    如您所见,您的函数接受一个经典的C函数指针。它不会接受任何类型的函数对象。这里要注意的是,没有捕获的lambda可以转换为函数指针。

    如保留上述声明:

    AddToInt([](int b) { return b + 1; }); // ok, non-capturing lambda conversion to function pointer
    
    AddToInt([a](int b) { return a + b; }); // error cannot convert capturing lambda to function pointer
    

    原因很容易理解。非捕获lambda可以等效于自由函数,但捕获lambda具有状态(由捕获集形成),因此它比简单的、经典的自由函数“更多”。

    正如您所看到的,接受函数指针在很大程度上是一个古老的习语,因为这些限制(甚至不考虑传递任何类型的函数对象,例如 operator() 定义的)。

    对于接受任何类型的可调用对象,通常有两个选项:通用模板或标准 std::function 对象。

    模板对象

    template <class Fn>
    int AddToInt1(Fn func)
    {
        return func(3);
    }
    

    现在你可以打电话了 AddToInt1 任何类型的呼叫。取决于推导的类型 Fn 类型可以使用此方法获得零开销。缺点是您可以接受任何类型,包括不可调用的类型,或者参数或返回类型不正确的类型。概念将减轻这些不利因素的大部分。

    AddToInt1([](int b) { return b + 1; }); // OK
    
    AddToInt1([a](int b) { return a + b; }); // OK
    

    您还可能希望添加完美的转发(为了简洁起见,在示例中省略了)。

    STD::功能

    另一条路线是 STD::功能 :

    int AddToInt2(std::function<int(int)> func)
    

    这里的缺点是 STD::功能 对象。它使用类型擦除,这会增加大量的性能损失(根据您的使用情况,这是完全可以接受的)。

    AddToInt2([](int b) { return b + 1; }); // OK
    
    AddToInt2([a](int b) { return a + b; }); // OK
    

    现在,一旦您了解了上面的要点,您的代码还有一些问题需要解决:

    [a] (int b) { a += b;};
    

    首先,您知道这个lambda不会返回任何内容吗?此外,它还尝试修改由值捕获的 a 这是非法的,因为lambda的 运算符() const 默认情况下是有充分理由的。如果希望lambda修改外部 捕获变量,然后需要通过引用捕获它:

    [&a] (int b) { a += b;};
    

    现在你必须非常小心不要以一个悬而未决的参考结束。

    但我怀疑你的意思是:

    AddToInt([a] (int b) { return a + b;});
    

    但这纯粹是我的猜测。


    下面是一个完全有效的例子:

    template <class Fn>
    int AddToInt1(Fn func)
    {
        return func(3);
    }
    
    int AddToInt2(std::function<int (int)> func)
    {
        return func(3);
    }
    
    int GetCoolInt()
    {
        int a = 3;
    
        return AddToInt1([a] (int b) { return a + b;}); // OK
        //return AddToInt2([a] (int b) { return a + b;}); // OK
    }
    

    这里有一些我刚刚提到的重要的观点,但是对它们的详细阐述就相当于编写一个关于lambdas及更高版本的完整教程,这超出了本网站的范围。总之,你必须自己研究这个课题。