Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Capture by move of variable used in the same statement [duplicate]

Edit: Marked as duplicate (sort of, there is no undefined behavior, only unspecified), I was fooled by the artificial complexity of lambda capture by move, which is not relevant.

My coworkers and I have discovered a strange discrepancy between GCC 6, 7 and Clang when we try to capture a variable by move after using it by const reference in the same statement. A minimal example:

#include <iostream>
#include <string>

struct A
{
    template <class F> void call (F f)
    {
         f();
    }
};

A test(const std::string& s)
{
    std::cout << "in method: " << s << std::endl;
    return {};
}

int main()
{
    std::string s = "hello";
    test(s).call([s = std::move(s)] { std::cout << "in lambda: " << s << std::endl;});
    return 0;
}

Wandbox link: https://wandbox.org/permlink/TMoB6EQ7RxTJrxjm

Output in GCC 6.3:

in method: 
in lambda: hello

Output in GCC 7.2 and Clang:

in method: hello
in lambda: hello

Note that using two statements, i.e. auto t = test(s); t.call(...); gives the same (second) result with GCC 6.3. We were naively expecting the second output everywhere: is it a GCC 6 bug, an undefined behavior or a standard change?

For info our "real-life" case is asynchronous call: test is the call, struct A is the result which expects a callback.

like image 876
Frédéric Samier Avatar asked Dec 09 '25 12:12

Frédéric Samier


1 Answers

In C++14, it's unspecified whether test(s) is evaluated first or if the lambda expression is evaluated first. There's no undefined behavior, however (assuming that test doesn't trigger UB with a moved-from string).

In C++17, test(s) is always evaluated before the lambda.

like image 95
T.C. Avatar answered Dec 12 '25 02:12

T.C.



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!