通过移动同一语句中使用的变量进行捕获

Capture by move of variable used in the same statement

编辑:标记为重复(有点,没有未定义的行为,只有未指定),我被移动捕获 lambda 的人为复杂性所愚弄,这无关紧要。

我的同事和我发现 GCC 6、7 和 Clang 之间存在奇怪的差异,当我们尝试通过移动 after 在同一语句中通过 const 引用使用它来捕获变量时. 一个最小的例子:

#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;
}

魔杖盒link:https://wandbox.org/permlink/TMoB6EQ7RxTJrxjm

GCC 6.3 中的输出:

in method: 
in lambda: hello

GCC 7.2 和 Clang 中的输出:

in method: hello
in lambda: hello

请注意,使用两个语句,即 auto t = test(s); t.call(...); 给出与 GCC 6.3 相同的(第二个)结果。我们天真地期待到处都是第二个输出:它是 GCC 6 错误、未定义的行为还是标准更改?

请注意,我们的 "real-life" 案例是异步调用:test 是调用,struct A 是需要回调的结果。

在 C++14 中,未指定是先计算 test(s) 还是先计算 lambda 表达式。但是,没有未定义的行为(假设 test 不会触发 UB 并从 string 移出)。

在 C++17 中,test(s) 总是在 lambda 之前求值。