std::optional 是否在调用包含的对象函数时转发右值性?

Does std::optional forwards rvalueness when contained object functions are called?

C++ 鲜为人知的特性是 ref-qualifiers for member functions

它在大多数情况下都像我预期的那样工作,但似乎 std::optional 没有将其即将消亡的知识转发给包含的对象成员函数。 例如考虑以下代码:

#include <chrono>
#include <iostream>
#include <optional>

struct Noisy {
    Noisy(const std::string& data): data_(data){
    }
    ~Noisy(){
        std::cout << "Goodbye" << std::endl;
    }
    std::string data_; 
    const std::string& data() const & {
        std::cout << "returning data by ref" << std::endl;
        return data_;
    }
    std::string data() && {
        std::cout << "returning data by move" << std::endl;
        return std::move(data_);
    }
};
int main() {
    for (const auto chr: Noisy{"Heeeeeeeeeeeeeeeeello wooooorld"}.data()){
        std::cout << chr;
    }
    std::cout << std::endl;
    for (const auto chr: std::optional<Noisy>{"Heeeeeeeeeeeeeeeeello wooooorld"}->data()){
        std::cout << chr;
    }
    std::cout << std::endl;
}

输出为:

returning data by move
Goodbye
Heeeeeeeeeeeeeeeeello wooooorld
returning data by ref
Goodbye
(crash in clang with sanitizer or garbage(UB))

我希望临时 std::optional 能够调用正确的 (data() &&) 函数,但它似乎没有发生。 这是语言限制,还是 std::optional 没有正确的机制?

完整 godbolt link.

注意:我的动机是四处寻找,看看我是否可以聪明地在基于范围的 for 循环中更安全地使用我的 类,但实际上不值得付出努力,这个问题主要是关于学习语言。

重载运算符箭头不能做你想做的;它总是以指针结束。

当且仅当 x 是一个指针时,

x->y 被标准定义为 (*x).y;否则就是 (x.operator->())->y。此递归仅在您点击指针时终止。1

并且没有指向临时类型的指针。试试这个:

 const auto chr: (*std::optional<Noisy>{"Heeeeeeeeeeeeeeeeello wooooorld"}).data()

Which does call the rvalue method。 (通过@largest_prime)。


1这个递归也可以做到Turing complete computation.