简体   繁体   中英

Auto in lambda bug in g++-5?

The following code, contains two identical lambdas, but when compiled with g++5 produces different answers. The lambda which uses the auto keyword in the argument declaration compiles fine, but returns zero instead of the correct count of 1. Why? I should add the code produces the correct output with 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. 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 . 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. 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.

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. 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 :

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. The zero size is indication enough that count will return zero, simply because there are no elements in an empty set. 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:

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). 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.

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. 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 :-)

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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