简体   繁体   English

g++-5 中的 lambda 错误中的自动?

[英]Auto in lambda bug in g++-5?

The following code, contains two identical lambdas, but when compiled with g++5 produces different answers.以下代码包含两个相同的 lambda,但在使用 g++5 编译时会产生不同的答案。 The lambda which uses the auto keyword in the argument declaration compiles fine, but returns zero instead of the correct count of 1. Why?在参数声明中使用 auto 关键字的 lambda 编译得很好,但返回零而不是正确的计数 1。为什么? I should add the code produces the correct output with g++-6.我应该添加代码,使用 g++-6 生成正确的输出。

g++-5 -std=c++14 file.cc
./a.out
Output:
f result=0  (incorrect result from lambda f)
...
g result=1  (correct result from lambda g)
...

#include<iostream>
#include<set>
#include<vector>
#include<algorithm>
using namespace std;

enum obsMode { Hbw, Lbw, Raw, Search, Fold};

int main(int , char **)
{
    static set<obsMode> legal_obs_modes = {Hbw, Lbw, Raw, Search, Fold};
    vector<obsMode> obs_mode = { Hbw,Lbw,Hbw,Lbw};
    // I named the lambdas to illustrate the issue 
    auto f =    [&] (auto i) -> void
        {
            cout << "f result=" << legal_obs_modes.count(i) << endl;
        };
    auto g =    [&] (obsMode i) -> void
        {
            cout << "g result=" << legal_obs_modes.count(i) << endl;
        };
    // f does not work
    for_each(obs_mode.begin(), obs_mode.end(), f);
    // g does work
    for_each(obs_mode.begin(), obs_mode.end(), g);
    return 0;
}

A somewhat deep dive into the issue shows up what the problem is.深入研究这个问题就会发现问题是什么。

It seems to exist all the way from 5.1 up to 6.1 (according to Compiler Explorer ), and I notice this targeted-for-fix-in-6.2 bug report which may be related.它似乎从 5.1 到 6.1 一直存在(根据Compiler Explorer ),我注意到这个可能相关的针对 6.2 修复的目标错误报告 The errant code was:错误的代码是:

#include <iostream>
#include <functional>
int main() {
    static int a;
    std::function<void(int)> f = [](auto) { std::cout << a << '\n'; };
    a = 1;
    f(0);
}

and it printed 0 rather than the correct 1 .它打印了0而不是正确的1 Basically, the use of statics and lambdas caused some troubles in that a static variable was made available to the lambda, at the time of lambda creation, as a copy.基本上,静态变量和 lambda 的使用引起了一些麻烦,因为在创建 lambda 时,静态变量作为副本可供 lambda 使用 For that particular bug report, it meant that static variable always seemed to have the value it had when the lambda was created, regardless of what you'd done with it in the meantime.对于那个特定的错误报告,这意味着静态变量似乎总是具有创建 lambda 时所具有的值,无论您在此期间对它做了什么。

I originally thought that this couldn't be related since the static in this question was initialised on declaration, and never changed after lambda creation.我最初认为这不可能相关,因为这个问题中的静态是在声明时初始化的,并且在 lambda 创建后从未改变。 However, if you place the following line before creating the lambdas and as the first line in each lambda, and compile (again, on Compiler Explorer) with x86-64 6.1 with options --std=c++14 :但是,如果您在创建 lambdas 之前放置以下行并将其作为每个 lambda 中的第一行,并使用选项--std=c++14使用x86-64 6.1编译(再次在编译器资源管理器上):

cout << &legal_obs_modes << ' ' << legal_obs_modes.size() << '\n';

then you'll see something very interesting (I've reformatted a little for readability):然后你会看到一些非常有趣的东西(为了可读性,我重新格式化了一点):

0x605220 5

0x605260 0 f result=0
0x605260 0 f result=0
0x605260 0 f result=0
0x605260 0 f result=0

0x605220 5 g result=1
0x605220 5 g result=1
0x605220 5 g result=1
0x605220 5 g result=1

The failing f ones have a size of zero rather than five, and a totally different address.失败的f个的大小为 0 而不是 5,并且地址完全不同。 The zero size is indication enough that count will return zero, simply because there are no elements in an empty set.零大小足以表明count将返回零,仅仅是因为空集中没有元素。 I suspect the different address is a manifestation of the same problem covered in the linked bug report.我怀疑不同的地址是链接错误报告中涵盖的相同问题的表现。

You can actually see this in the Compiler Explorer assembler output, where the two different lambdas load up a different set address:您实际上可以在Compiler Explorer汇编器输出中看到这一点,其中两个不同的 lambda 加载了不同的设置地址:

mov    edi, 0x605260 ; for f
mov    edi, 0x605220 ; for g

Making the set automatic instead of static causes the problem to go away entirely.使设置自动而不是静态会导致问题完全消失。 The address is the same within both lambdas and outside of them, 0x7ffd808eb050 (on-stack rather than in static area, hence the vastly changed value).地址在 lambda 内部外部都相同, 0x7ffd808eb050 (堆栈上而不是静态区域,因此值发生了巨大变化)。 This tends to gel with the fact that statics aren't actually captured in lambdas, because they're always supposed to be at the same address, so can just be used as-is.这往往与静态实际上并未在 lambda 中捕获的事实相结合,因为它们总是应该位于相同的地址,因此可以按原样使用。

So, the problem appears to be that the f lambda, with its auto -deduced parameter, is making a copy of the static data instead of using it in-situ.因此,问题似乎在于f lambda 及其auto推导的参数正在制作静态数据的副本,而不是原位使用它。 And I don't mean a good copy, I mean one akin to being from photo-copier that ran out of toner sometime in 2017 :-)我的意思不是好的副本,我的意思是类似于 2017 年某个时候碳粉用完的复印机:-)

So, in answer to your specific question about whether this was a bug or not, I think the consensus would be a rather emphatic yes.因此,在回答您关于这是否是错误的具体问题时,我认为共识将是相当强调是的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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