Skip to content

Commit

Permalink
Moved caveats of capturing by reference to a separate section.
Browse files Browse the repository at this point in the history
  • Loading branch information
Lastique committed Jan 28, 2024
1 parent 56afb11 commit 10d2255
Showing 1 changed file with 55 additions and 37 deletions.
92 changes: 55 additions & 37 deletions doc/scope_guards.qbk
Original file line number Diff line number Diff line change
Expand Up @@ -227,43 +227,6 @@ Scope guards described in this section are move-constructible (but not assignabl
move- or copy-constructible as well. After moving, the moved-from scope guard becomes inactive. If a moved-from scope guard is active
on destruction, the behavior is undefined.

When using scope guards, users should make sure that all variables captured by reference are still in a valid state upon the scope guard
destruction. This especially pertains to lambda functions that capture variables by reference by default (i.e. where the capture clause
starts with `&`). Consider the following example:

std::unique_ptr< int > bad(std::unique_ptr< int > ptr)
{
assert(ptr != nullptr);

boost::scope::scope_exit guard{[&]
{
std::cout << "processed: " << *ptr << std::endl;
}};

process(*ptr);

return ptr; // moves from ptr, leaving it null for the scope guard action
}

In cases like this consider capturing by value in the function object or moving the scope guard to a deeper scope, so that it executes
its action before the variables captured by reference become invalid.

std::unique_ptr< int > good(std::unique_ptr< int > ptr)
{
assert(ptr != nullptr);

{
boost::scope::scope_exit guard{[&]
{
std::cout << "processed: " << *ptr << std::endl;
}};

process(*ptr);
} // <- scope guard action runs here, before ptr is moved-from

return ptr;
}

[endsect]

[section:condition_functions Scope guard condition functions]
Expand Down Expand Up @@ -560,6 +523,61 @@ directly.

[endsect]

[section:capture_by_reference_caveats Caveats of capturing by reference]

When using scope guards, users should make sure that all variables captured by reference are still in a valid state upon the scope guard
destruction. This especially pertains to lambda functions that capture variables by reference by default (i.e. where the capture clause
starts with `&`). Consider the following example:

void bad(std::unique_ptr< int > ptr)
{
assert(ptr != nullptr);

boost::scope::scope_exit guard{[&]
{
std::cout << "processed: " << *ptr << std::endl;
}};

process(std::move(ptr)); // may consume ptr, leaving it null for the scope guard action
}

Or a slightly less obvious version of it, involving implicit move of the returned value:

std::unique_ptr< int > bad(std::unique_ptr< int > ptr)
{
assert(ptr != nullptr);

boost::scope::scope_exit guard{[&]
{
std::cout << "processed: " << *ptr << std::endl;
}};

process(*ptr);

return ptr; // moves from ptr, leaving it null for the scope guard action
}

In cases like this consider capturing by value in the function object or moving the scope guard to a deeper scope, so that it executes
its action before the variables captured by reference become invalid.

std::unique_ptr< int > good(std::unique_ptr< int > ptr)
{
assert(ptr != nullptr);

{
boost::scope::scope_exit guard{[&]
{
std::cout << "processed: " << *ptr << std::endl;
}};

process(*ptr);
} // <- scope guard action runs here, before ptr is moved-from

return ptr;
}

[endsect]

[section:runtime_defined Setting up scope exit actions at run time]

It is possible to use scope guard classes to implement scope exit actions that are initialized at run time. One way to do this is to use
Expand Down

0 comments on commit 10d2255

Please sign in to comment.