在编写 UE4 规范时,我发现有时传入 lambda 的值会导致崩溃或不可预测的结果,而其他时候却不会。
例如,在下面的示例中调用
Matcher
内的 It()
对象上的任何方法都会导致崩溃(当我在 Rider 调试器中检查它们时,所有值都是垃圾数据):
void FPF2AbilityBoostRuleOptionMatcherSpec::Define()
{
Describe(TEXT("when there are no rule options"), [=, this]()
{
const TArray<FPF2AbilityBoostRuleOption> RuleOptions = {};
Describe(TEXT("CanApplyAbilityBoost()"), [=, this]()
{
FPF2AbilityBoostRuleOptionMatcher Matcher(RuleOptions);
It(TEXT("returns `false` for all abilities"), [=, this, &Matcher]()
{
for (const auto& AbilityScoreType : TEnumRange<EPF2CharacterAbilityScoreType>())
{
TestFalse(
FString::Format(TEXT("CanApplyAbilityBoost({0})"), { PF2EnumUtils::ToString(AbilityScoreType)}),
Matcher.CanApplyAbilityBoost(AbilityScoreType)
);
}
});
});
// ...
});
// ...
}
将其切换为按值传递会导致 IDE 错误,但它的编译和崩溃与我使用引用时的情况相同:
void FPF2AbilityBoostRuleOptionMatcherSpec::Define()
{
Describe(TEXT("when there are no rule options"), [=, this]()
{
const TArray<FPF2AbilityBoostRuleOption> RuleOptions = {};
Describe(TEXT("CanApplyAbilityBoost()"), [=, this]()
{
FPF2AbilityBoostRuleOptionMatcher Matcher(RuleOptions);
It(TEXT("returns `false` for all abilities"), [=, this, Matcher]()
{
for (const auto& AbilityScoreType : TEnumRange<EPF2CharacterAbilityScoreType>())
{
TestFalse(
FString::Format(TEXT("CanApplyAbilityBoost({0})"), { PF2EnumUtils::ToString(AbilityScoreType)}),
// Next line gets highlighted in red in Rider but compiles. Error is:
// Cannot convert 'this' argument from type 'FPF2AbilityBoostRuleOptionMatcher const' to type
// 'FPF2AbilityBoostRuleOptionMatcher': function is missing 'const' qualifier`
Matcher.CanApplyAbilityBoost(AbilityScoreType)
);
}
});
});
// ...
});
// ...
}
尽管如此(但是必须在每个测试用例中构建
Matcher
):
void FPF2AbilityBoostRuleOptionMatcherSpec::Define()
{
Describe(TEXT("when there are no rule options"), [=, this]()
{
const TArray<FPF2AbilityBoostRuleOption> RuleOptions = {};
Describe(TEXT("CanApplyAbilityBoost()"), [=, this]()
{
It(TEXT("returns `false` for all abilities"), [=, this]()
{
FPF2AbilityBoostRuleOptionMatcher Matcher(RuleOptions);
for (const auto& AbilityScoreType : TEnumRange<EPF2CharacterAbilityScoreType>())
{
TestFalse(
FString::Format(TEXT("CanApplyAbilityBoost({0})"), { PF2EnumUtils::ToString(AbilityScoreType)}),
Matcher.CanApplyAbilityBoost(AbilityScoreType)
);
}
});
});
});
// ...
});
// ...
}
为什么?在引用该对象之前,是否会因某种原因调用该对象的析构函数?
正如我怀疑的那样,外部作用域中的变量正在分配堆栈,然后当它们被使用时,它们指向垃圾内存。为了避免崩溃,必须声明变量
static
,这确保在整个运行时为这些值保留内存。