В чем разница между сопрограммой и функцией со статическими переменными?

#c #c 20 #coroutine

Вопрос:

Я узнал о том, что нового в C 20, и пытаюсь понять широко обсуждаемый вариант использования «генератора» для совместных подпрограмм. Я попытался создать здесь небольшой пример, но приношу извинения, если есть какая-то ошибка:

 generator<int> Generate() {
    int i = 0;
    while(1) {
        co_yield i  ;
    }
}

int main()
{
    auto gen { Generate() };
    for (int x = 0; x < 10;   x) {
        gen.next();
        std::cout << gen.getValue() << std::endl;
    }
    return 0;
}
 

Но я не вижу, чем это отличается от функции со статическими переменными, такими как:

 auto gen() {
    static int i = 0;
    return i  ;
}

int main()
{
    for (int x = 0; x < 10;   x)
        std::cout << gen() << std::endl;
    return 0;
}
 

Я думаю, что, возможно, смогу понять, насколько асинхронный ввод-вывод полезен, особенно с co_await ключевым словом, но для этого примера генератора я уверен, что что-то неправильно понимаю в том, как их следует использовать. Я был бы очень признателен за любые объяснения

Ответ №1:

Возможно, наиболее очевидное различие заключается в том, что static локальные переменные означают, что у вас фактически есть один экземпляр… весь. В то время как каждый генератор полностью независим.

 // with coroutines
assert(Generator().next() == 0);
assert(Generator().next() == 0);
assert(Generator().next() == 0);
assert(Generator().next() == 0);
 

Каждый вызов Generator() создает новый генератор, каждый из которых начинает отсчет 0 . Так что каждый новый генератор next() дает мне ноль. Как и ожидалось.

Но это не относится к статическим локальным переменным:

 assert(gen() == 0);
assert(gen() == 1);
assert(gen() == 2);
assert(gen() == 3);
 

Таким образом, вы можете себе представить, что если бы вы хотели создать генератор, который давал бы вам бесконечный поток целых чисел, было бы неплохо, если бы вы могли надежно использовать эту функцию более одного раза во всей вашей программе.

Это не значит, что статические локальные переменные бесполезны. Просто они не подходят для данного конкретного случая использования.

Комментарии:

1. Ага! Теперь это то, о чем я вообще не думал. Отличный ответ, большое спасибо

2. Лямбда, с другой стороны, [i = 0]() mutable -> int { return i; }