...

/

Avoiding Dangling References

Avoiding Dangling References

Learn to prevent dangling coroutine references through parameter passing, member functions, lambdas, and guidelines.

The fact that a coroutine can be passed around in our code means that we need to be very careful about the lifetime of parameters we pass to a coroutine to avoid dangling references. The coroutine frame contains copies of the objects that normally live on the stack, such as local variables and parameters passed to the coroutine. If a coroutine accepts an argument by reference, the reference is copied, not the object. This means that we can easily end up with dangling references when following the usual guidelines for function parameters; that is, pass objects that are expensive to copy by reference to const.

Passing parameters to coroutines

The following coroutine uses a reference to a const std::string:

Press + to interact
auto coroutine(const std::string& str) -> Resumable {
std::cout << str;
co_return;
}

Suppose we have a factory function that creates and returns the coroutine, like this:

Press + to interact
auto coro_factory() {
auto str = std::string{"ABC"};
auto res = coroutine(str);
return res;
}

And finally, a main() function that uses the coroutine:

Press + to interact
main.cpp
chapter_12.h
Resumable.h
int main() {
auto coro = coro_factory();
coro.resume();
}

This code exhibits undefined behavior as the std::string object containing the string "ABC" is no longer alive when the coroutine tries to access it. Hopefully, this doesn’t come as a surprise to us. This problem is similar to having a lambda capture a variable by reference, and then passing the lambda to some other code without keeping the referenced object alive. A similar example can be achieved when passing around a lambda capturing variables by reference:

Press + to interact
auto lambda_factory() {
auto str = std::string{"ABC"};
auto lambda = [&str]() { // Capture str by reference
std::cout << str;
};
return lambda; // Ops! str in lambda becomes
} // a dangling reference
int main() {
auto f = lambda_factory();
f(); // Undefined behavior
}

As we can see, the same problem can happen with lambdas. In the “Essential C++ Techniques” chapter, we warned you about capturing references with lambdas, and it is usually better to avoid this by capturing by value instead.

The solution to avoid ...