[英]C++ higher order functions: different results when declaring multiple functions
I was plying with higher order functions, and I started getting different results when I created two instances and assigning them to variables.我正在使用高阶函数,当我创建两个实例并将它们分配给变量时,我开始得到不同的结果。
I reduced the issue to the following example:我将问题简化为以下示例:
#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";
}
My expectation was to get 10, followed by 20 from both sequences.我的期望是从两个序列中获得 10 个,然后是 20 个。 Instead, depending on the compiler, I get:
相反,根据编译器,我得到:
➜ 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
Is there something fundamentally wrong with the above code?上面的代码有什么根本错误吗? I get no compiler warnings or clang-tidy issues, at least with my current settings.
至少在我当前的设置下,我没有收到编译器警告或整洁的问题。
Let's look at this code:让我们看一下这段代码:
auto paint(const ColorGetter &f)
{
return [&]() { return f(); };
}
The f
parameter only exists for the lifetime of the function paint
. f
参数仅在函数paint
的生命周期内存在。 Your lambda function captures it by reference (that's the [&]
bit), and so your lambda capture is referencing a variable (the reference f
) that no longer exists after the function ends.您的 lambda 函数通过引用(即
[&]
位)捕获它,因此您的 lambda 捕获正在引用函数结束后不再存在的变量(引用f
)。 As a result, the object you're returning holds a dangling reference to the function.因此,您返回的对象包含对该函数的悬空引用。 You're seeing undefined behavior here, which is why the result varies across compilers.
您在这里看到未定义的行为,这就是结果因编译器而异的原因。
To fix this, change the code to read要解决此问题,请将代码更改为
auto paint(const ColorGetter &f)
{
return [=]() { return f(); };
}
This will cause the lambda capture to make a copy of the value stored in f
(the pointer to the function in question), which will outlive the paint
function.这将导致 lambda 捕获复制存储在
f
中的值(指向相关函数的指针),这将比paint
函数更有效。
You aren't keeping track of lifetime.你没有跟踪生命周期。
auto paint(const ColorGetter &f)
{
return [&]() { return f(); };
}
this captures a reference to f
.这捕获了对
f
的引用。 You should almost NEVER use [&]
when the lambda outlives the scope it is created in.当 lambda 超出创建它的范围时,您几乎不应该使用
[&]
。
The f
is: f
是:
auto painter1 = paint(colorA);
a temporary pointer created on this line.在此行上创建的临时指针。 It is discarded at the end of the statement.
它在语句结束时被丢弃。
So your code, when it does f()
, exhibits undefined behavior -- you are following a dangling reference.因此,您的代码在执行
f()
时会表现出未定义的行为——您正在关注一个悬空引用。
The easy fixes include:简单的修复包括:
auto paint(const ColorGetter &f)
{
return [=]() { return f(); };
}
I would also get rid of the reference-to-pointer:我也会摆脱引用指针:
auto paint(const ColorGetter f)
{
return [=]() { return f(); };
}
while I am at it.当我在它的时候。 Taking
const&
to arguments blindly is a bad habit.盲目地将
const&
用于参数是一个坏习惯。 As is taking things by value blindly.就像盲目地以价值衡量一样。 Know what you are passing.
知道你正在传递什么。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.