C++ 高阶函数:声明多个函数时的不同结果

C++ higher order functions: different results when declaring multiple functions

我一直在使用高阶函数,当我创建两个实例并将它们分配给变量时,我开始得到不同的结果。

我将问题简化为以下示例:

#include <iostream>

using ColorGetter = int(*)(void);

auto paint(const ColorGetter &f)
{
    return [&]() { return f(); };
}

int colorA() { return 10; }

int colorB() { return 20; }

int main()
{
    auto painter1 = paint(colorA);
    auto painter2 = paint(colorB);
    std::cout << "painter 1 : " << painter1() << "\n";
    std::cout << "painter 2 : " << painter2() << "\n";

    auto p1 = [] () { return colorA(); };
    auto p2 = [] () { return colorB(); };
    std::cout << "p 1 : " << p1() << "\n";
    std::cout << "p 2 : " << p2() << "\n";
}

我的期望是从两个序列中得到 10 个,然后是 20 个。 相反,根据编译器,我得到:

➜  tmp clang++-13 -o out.gcc wrong.cpp&& ./out.gcc       
painter 1 : 10
painter 2 : 20
p 1 : 10
p 2 : 20
➜  tmp g++-11 -o out.gcc wrong.cpp && ./out.gcc
painter 1 : 20
painter 2 : 20
p 1 : 10
p 2 : 20

上面的代码有什么根本性的错误吗? 我没有收到编译器警告或 clang-tidy 问题,至少在我当前的设置下是这样。

我们来看这段代码:

auto paint(const ColorGetter &f)
{
    return [&]() { return f(); };
}

f 参数仅在函数 paint 的生命周期内存在。您的 lambda 函数通过引用(即 [&] 位)捕获它,因此您的 lambda 捕获引用的变量(引用 f)在函数结束后不再存在。结果,您返回的对象持有对该函数的悬空引用。您在这里看到未定义的行为,这就是结果因编译器而异的原因。

要解决此问题,请将代码更改为

auto paint(const ColorGetter &f)
{
    return [=]() { return f(); };
}

这将导致 lambda 捕获复制存储在 f 中的值(指向相关函数的指针),这将超过 paint 函数。

您没有跟踪生命周期。

auto paint(const ColorGetter &f)
{
  return [&]() { return f(); };
}

这会捕获对 f 的引用。当 lambda 超过它创建的范围时,你几乎不应该使用 [&]

f是:

auto painter1 = paint(colorA);

在此行创建的临时指针。它在语句末尾被丢弃。

因此,当您的代码执行 f() 时,它会表现出未定义的行为 -- 您正在跟随悬空引用。

简单的修复包括:

auto paint(const ColorGetter &f)
{
  return [=]() { return f(); };
}

我也会去掉 reference-to-pointer:

auto paint(const ColorGetter f)
{
  return [=]() { return f(); };
}

当我在做的时候。盲目拿const&去争论是个坏习惯。就像盲目地按价值看待事物一样。知道你在传递什么。