采用聚合参数的协程中的双重无错误

问题描述 投票:0回答:0

以下代码在最新版本的 GCC 和 Clang 中可以正常工作,但会导致 GCC 11 到 GCC 12 中的双重释放,如 https://godbolt.org/z/z66qYaxcG 所示。

#include <coroutine>
#include <iostream>
#include <string>

struct Coro{
    struct Promise;
    struct Awaiter{
        bool await_ready(){return true;}
        void await_resume(){}
        void await_suspend(std::coroutine_handle<Promise> coro){}
    };
    struct Promise{
        Coro get_return_object(){return {};}
        auto initial_suspend() noexcept{return std::suspend_never{};}
        auto final_suspend() noexcept{return std::suspend_never{};}
        void unhandled_exception(){}
        Awaiter await_transform(const Coro&){
            return {};
        }
        void return_void(){}
    };
    using promise_type = Promise;
};

struct Aggregate{
    //(*)
    //Aggregate(const std::string& s):s{s}{}
    std::string s;
};

static_assert(std::is_aggregate_v<Aggregate>);

Coro f1(Aggregate){
    co_return;
}

Coro f2(std::string){
    co_return;
}

Coro g(){
    std::string foo{"foo"};
    // 1.
    co_await f1(Aggregate{"/"+foo});
    // 2.
    //co_await f2("/" + foo);
    // 3.
    //Coro c = f1(Aggregate{"/"+foo});
    //co_await c;
}

int main(){
    g();
}

我想知道这是否是由于编译器错误或未定义的行为造成的,在前一种情况下,是否有任何与之相关的错误报告,在后一种情况下,为什么它是未定义的行为。我做了一些实验,发现双重释放在以下任何修改下都会消失,

  • 在标记为
    (*)
    的行附近添加用户定义的构造函数,以便
    Aggregate
    不再是聚合
  • 直接传递字符串(取消注释
    2.
    附近的行并调用
    f2
  • 将对
    f1
    的调用放在与
    co_await
    不同的行中(取消注释
    3.
    附近的行)

此外,我还观察到,将

f1
更改为通过引用而不是通过值获取参数并不能解决问题。

c++ c++20 coroutine memory-corruption aggregate-initialization
© www.soinside.com 2019 - 2024. All rights reserved.