{{< Production >}}
If you get to the end of step 1, the refactoring is possible - it will produce a valid result.
This recipe is designed to do Extract Function and nothing else. If you want something else, there are other refactorings for that.
You can commit at the end of any numbered step. At each such point, the code will compile and produce exactly the same results as the original code.
This recipe only works on rvalue expressions or on whole blocks (surrounded by braces) or a single for/while/if statement. It does not work on single statements, due to variable lifetime changes. If you need to extract statements but not a block, first execute the Extract Block refactoring
For a block/for/while/if, surround with:
[&]() {
// ...
}();
For an expression, surround it with:
[&]() { return
// ...
;}()
Compile single file. Possible errors:
not all control paths return a value
. You have an early return. Back up and either Eliminate Early Return/Continue/Break (fix link) or extract something different.a break/continue statement may only be used within ...
. You have a break/continue. Back up and either Eliminate Early Return/Continue/Break (fix link) or extract something different.
Search the new lambda for any return statements (using find). If there are any returns:
If it's obvious that all code paths return, then add a return
before the lambda:
return [&]() {
//...
}();
If it's not obvious that all code paths return, then back up and either Eliminate Early Return/Continue/Break (fix link) or try something different.
- Assign the lambda to
Applesauce
and call it. (Extract Variable)
For example,
[&]() {
// ...
}();
becomes:
auto Applesauce = [&]() {
// ...
}; Applesauce();
Compile to make sure you didn't typo.
Set the return type on the lambda, even if it's void
. In Visual Studio, the IntelliSense tooltip over auto
will tell you the type.
auto Applesauce = [&]() -> SOMETYPE {
// ...
};
Compile to make sure you got the return type correct.
Replace [&]
with [this]
(or []
in a free function) and compile.
For each error about a variable that must be captured:
- Copy the variable name
- Paste it in to the capture list, prefixed with
&
. - Repeat until green.
For example,
auto Applesauce = [this, &foo]() -> bool {
cout << foo;
};
The order of the capture list will influence the order of the parameters of the final function. If you want the parameters in a particular order, now is a good time to reorder the capture list.
- Select the capture list (but not 'this') and Cut
- Paste into the argument list, removing
&
s. - Paste into the parameter list and inject
const auto
in front of each parameter - Compile.
- If you get an error because the variable is modified, make it non-const
For example,
auto Applesauce = [this, &i, &s]() -> void
{
cout << ++i << s;
};
Applesauce();
becomes:
auto Applesauce = [this](auto &i, const auto &s) -> void
{
cout << ++i << s;
};
Applesauce(i, s);
For each argument:
- Go-to-definiton on the argument
- Copy the variable type
- Paste into the lambda parameter list
- Compile
For example,
int i = ...
std::string s = ...
auto Applesauce = [this](auto &i, const auto &s) -> void
{
std::cout << ++i << s;
};
Applesauce(i, s);
becomes:
int i = ...
std::string s = ...
auto Applesauce = [this](int &i, const std::string &s) -> void
{
std::cout << ++i << s;
};
Applesauce(i, s);
When the parameter is a pointer type, it's important to understand how the placement of the const keyword affects the parameter type.
For example, given a local variable in Column* pCol = ...
, the original type is "Pointer to Column
", so the pointer is what
must be const (so we know it's not reseated to point to another object). It's also important to specify that the Column
itself is
const to avoid losing the implicit constness in the original code as we make the data flow explicit.
const Column* // WRONG - this is a non-const pointer to const column
Column const* // WRONG - same as above
Column* const // BETTER - this is a const pointer to non-const column
Column const * const // BEST - this is a const pointer to const column
To avoid ambiguity, always place the const keyword to the right of what you want to make const.
- Remove
this
from the capture list - Compile
- If the compile fails, undo
For example,
auto Applesauce = [this]() -> void {
// ...
};
becomes:
auto Applesauce = []() -> void {
// ...
};
- If
this
is captured, use 8A. - If
this
is not captured, use 8B.
- Cut the lambda statement and paste it outside the current function.
- Remove
= [this]
. - Mark the new method as
const
. - Copy the signature line.
- Add
SomeClass::
- Paste the signature in to the class declaration in a private section.
- Compile and remove
const
if necessary.
For example,
class SomeClass
{
private:
auto Applesauce () const -> void;
}
auto SomeClass::Applesauce () const -> void {
// ...
};
- Cut the lambda statement and paste it above the current function.
- Remove
= []
- Wrap it in an anonymous namespace
- Compile and resolve any errors:
- If the free function uses typedefs/aliases or classes nested in the original class, convert the free function to a private static function of the original class.
For example,
namespace {
auto Applesauce () -> void {
...
};
}
- Select the return value (after the
->
) and cut it. - Delete the
->
- Select the word
auto
and paste the return value over it. - Remove the trailing semicolon.
For example,
namespace {
auto Applesauce () -> void {
...
};
}
becomes:
namespace {
void Applesauce () {
...
}
}