回答
可读性和性能。
详细信息描述为以下示例的注释。
用例
个人例子
在Unreal Engine 4(C++ 游戏引擎)中,我有以下属性(引擎暴露的成员变量):
/// Floor Slope.
UPROPERTY
(
Category = "Movement",
VisibleInstanceOnly,
BlueprintGetter = "BP_GetFloorSlope",
BlueprintReadOnly,
meta =
(
ConsoleVariable = "Movement.FloorSlope",
DisplayName = "Floor Slope",
ExposeOnSpawn = true,
NoAutoLoad
)
)
float FloorSlope = -1.f;
这是玩家站立的地面坡度值(值 ∈ [0; 90)°),如果有的话。
由于引擎限制,它不能既不是std::optional 也不是TOptional。
我想出了一个解决方案来添加另一个可自我解释的变量bIsOnFloor。
bool bIsOnFloor = false;
FloorSlope 的唯一 C++ 内部设置器采用以下形式:
void UMovement::SetFloorSlope(const float& FloorSlope) noexcept
contract [[expects audit: FloorSlope >= 0._deg && FloorSlope < 90._deg]]
{
this->bIsOnFloor = true;
this->FloorSlope = FloorSlope;
AUI::UI->Debug->FloorSlope = FString::Printf(L"Floor Slope: %2.0f", FloorSlope);
};
添加特殊情况,其中FloorSlope 参数将采用-1.f 的参数,这将难以猜测且对用户不友好。
相反,我宁愿创建False enum 字段:
enum { False };
这样,我可以简单地重载 SetFloorSlope 函数,该函数采用直观的 False 而不是 -1.f。
void UMovement::SetFloorSlope([[maybe_unused]] const decltype(False)&) noexcept
{
this->bIsOnFloor = false;
this->FloorSlope = -1.f;
AUI::UI->Debug->FloorSlope = L"Floor Slope: —";
};
当玩家角色在滴答声中对地板施加重力时,我只需调用:
SetFloorSlope(FloorSlope);
... 其中FloorSlope 是float 值 ∈ [0; 90)°。
否则(如果它没有撞到地板),我会调用:
SetFloorSlope(False);
这种形式(与传递 -1.f 不同)更具可读性和自我解释性。
引擎示例
另一个例子可能是阻止或强制初始化。
上面提到的虚幻引擎 4 通常使用 FHitResult struct 包含有关跟踪的一次命中的信息,例如撞击点和该点的表面法线。
这个复杂的struct默认调用Init方法,给某些成员变量设置一些值。这可以强制或阻止(公共文档:FHitResult #constructor):
FHitResult()
{
Init();
}
explicit FHitResult(float InTime)
{
Init();
Time = InTime;
}
explicit FHitResult(EForceInit InInit)
{
Init();
}
explicit FHitResult(ENoInit NoInit)
{
}
Epic Games 定义了类似的enums,但添加了多余的enum 名称:
enum EForceInit
{
ForceInit,
ForceInitToZero
};
enum ENoInit {NoInit};
将NoInit 传递给FHitResult 的构造函数会阻止初始化,通过不初始化将在其他地方初始化的值会导致性能提升。
社区示例
FHitResult(NoInit) 在 DamirH 的 post综合游戏能力分析系列中的用法:
//A struct for temporary holding of actors (and transforms) of actors that we hit
//that don't have an ASC. Used for environment impact GameplayCues.
struct FNonAbilityTarget
{
FGameplayTagContainer CueContainer;
TWeakObjectPtr<AActor> TargetActor;
FHitResult TargetHitResult;
bool bHasHitResult;
public:
FNonAbilityTarget()
: CueContainer(FGameplayTagContainer())
, TargetActor(nullptr)
, TargetHitResult(FHitResult(ENoInit::NoInit))
, bHasHitResult(false)
{
}
// (…)