std::move 是否需要使用 co_yield 移动?

Is std::move required to move using co_yield?

假设我有一个在循环体中声明的 std::vector 并且 co_yielded:

some_generator<std::vector<int>> vector_sequence() {
    while (condition()) {
        std::vector<int> result = get_a_vector();
        result.push_back(1);
        co_yield std::move(result); // or just: co_yield result; 
    }
}

很明显,result 在被 co_yielded 之后不会再被使用(或者我大错特错),所以移动它是有意义的。我尝试了 co_yield 一个没有 std::move 的简单不可复制类型,但它没有编译,所以在通用代码中,人们会使用 std::move。编译器是否无法识别这个(编译器错误?),或者 co_yield 总是复制左值的语言是故意的,所以我必须 std::move?我知道 return 对作为局部变量的左值进行移动或某种其他类型的复制省略,这似乎与它没有太大区别。

我已阅读, which is related to this question, but does not answer it, and considered ,据我了解,与此问题无关。

隐式移动规则 ([class.copy.elision]/3) 适用于 returnco_return 语句以及 throw 表达式。它不适用于 co_yield.

原因是,在[class.copy.elision]/3中列举的上下文中,returnco_return语句或throw表达式的执行确保了隐式可移动实体的生命周期结束。例如,

auto foo() {
    std::string s = ...;
    if (bar()) {
        return s;
    }
    // return something else
}

这里,即使在return语句之后有代码,也保证如果 return语句执行,那么下面的任何代码可以看到s不会执行。这使得隐式移动 s.

变得安全

相比之下,co_yield只是挂起协程,并没有结束协程co_return ].因此,一般来说,在评估 co_yield result; 之后,协程可能稍后会恢复并再次使用完全相同的 result 变量。这意味着一般来说,将复制隐式转换为移动是不安全的;因此,该标准并未规定此类行为。如果你要搬家,写std::move.

如果语言在你的例子中允许隐式移动,它必须有特定的规则来确保,虽然变量 可以 在 [=14= 之后再次使用】,其实不然。在您的情况下,循环可能确实会立即结束,因此 result 变量的生命周期将在再次观察到其值之前结束,但通常您必须指定一组条件,在这些条件下可以保证是这样。然后,您可以建议仅在这些条件下才会发生隐式移动。