rspec/rules/S6391/cfamily/rule.adoc

57 lines
1.9 KiB
Plaintext
Raw Normal View History

== Why is this an issue?
Coroutines, introduced in {cpp}20, are functions in which execution can be suspended and resumed.
When a coroutine resumes, it takes over where it left thanks to the coroutine state.
A _coroutine state_ is an object which contains all the information a coroutine needs to resume its execution correctly:
local variables, copy of the parameters...
This means that if a coroutine has a parameter that is a reference to an object, this object must exist as long as the coroutine is not destroyed.
Otherwise, the reference stored in the _coroutine state_ will become a dangling reference and will lead to undefined behavior when the coroutine resumes.
The issue is raised for all coroutine parameters with reference-to-const semantics
(such as a `const` reference, a `std::string_view`, or a `std::span` with `const` elements)
that might be used after the coroutine is suspended.
To fix the issue, you can either pass the parameter by value,
or not use the parameter after the first suspension point (`co_await`, `co_yield`, or `initial_suspend`).
=== Noncompliant code example
2022-02-04 17:28:24 +01:00
[source,cpp]
----
generator<char> spell(const std::string& m) { // Noncompliant
for (char letter : m) {
co_yield letter;
}
}
void print() {
for (char letter : spell("hello world")) { // Here the parameter "m" binds to a temporary
std::cout << letter << '\n'; // and becomes dangling on the next iteration
}
}
----
=== Compliant solution
2022-02-04 17:28:24 +01:00
[source,cpp]
----
generator<char> spell(const std::string m) { // Compliant: take the argument by copy
for (char letter : m) {
co_yield letter;
}
}
void print() {
for (char letter : spell("hello world")) {
std::cout << letter << '\n';
}
}
----
=== Exceptions
This rule does not raise an issue for `std::reference_wrapper` parameters
taking it as a witness of the care taken to prevent the reference to become dangling.