在Java和C#中,我可以使用ReSharper、IntelliJ和Visual C#处理所需的分析,安全地提取一个方法,无论代码多么长或复杂。我相信它们不会改变代码的行为,即使对于我这个小脑袋来说,它太过复杂而难以理解。
然而,现有的C++工具不能给我同样的信心。CLion、ReSharper和Visual Assist在提取方法时都会引入行为变化。
那么我的选择有哪些呢?
一种选择是使用基于Tennent的对应原理的这个配方。您可以将其应用于整个代码块(用大括号括起来)或if
、while
或for
语句(它们会创建自己的作用域)。
将需要操作的代码块用如下方式包围:
[&]() {
// original code
}();
编译该文件。 可能的错误:
并非所有控制路径都返回值。 你有一个早期返回。回退并消除早期返回/Continue/Break或提取其他内容。
break/continue语句只能在... 你有一个break/continue。回退并消除早期返回/Continue/Break或提取其他内容。
检查新的lambda是否有返回语句。 如果有任何返回且很明显所有代码路径都会返回,则在lambda之后的下一行添加返回语句。如果有任何返回且不明显所有代码路径都将返回,则回退并消除早期返回/Continue/Break或尝试提取其他内容。
例如:
[&]() {
// ...
}();
变成:
auto Applesauce = [&]() {
// ...
};
Applesauce();
编译以确保您没有输错。
在lambda上设置返回类型(即使是void
)。在Visual Studio中,将鼠标悬停在auto
上将显示类型。
例如:
auto Applesauce = [&]() -> SOMETYPE {
// ...
};
编译以确保您正确获取了返回类型。
将[&]
替换为[this]
(或在自由函数中使用[]
)并进行编译。
对于每个关于必须捕获的变量的错误:
- 复制变量名称
- 将其粘贴到捕获列表中,前缀为&
- 重复此过程直至无误。
例如:
auto Applesauce = [this, &foo]() -> void {
cout << foo;
};
捕获列表的顺序将影响最终函数参数的顺序。如果你希望参数按特定顺序排列,现在是重新排序捕获列表的好时机。
对于每个被捕获的本地变量(除了this
)
- 转到变量的定义
- 复制变量声明(例如Column* pCol
)
- 粘贴到lambda参数列表中
- 使参数成为const和按引用传递
- 从捕获列表中删除变量
- 将变量传入调用
- 编译.
即:
Column* pCol = ...
auto Applesauce = [&pCol]() -> void { cout << pCol->name(); };
Applesauce();
变成
Column* pCol = ...
auto Applesauce = [](Column*& pCol) -> void { cout << pCol->name(); };
Applesauce(pCol);
如果捕获了this
,请使用6A。
如果未捕获this
,请使用6B。
= [this]
SomeClass::
例如:
auto SomeClass::Applesauce () const -> void {
// ...
};
= []
const
局部变量;这种立即调用的lambda(IIL)并不总是成为函数,因为它们只被使用一次。此外,由于我使用IIL进行初始化,我还有机会使我的对象成为const
(如果它们不需要是不可变的)。 - Nawaz