简体   繁体   English

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

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM