枚举不占用任何空间,并且是不可变的。 如果你使用const int color = 1;,那么你就可以解决可变性问题,但是如果有人使用了color(const int* p = &color;)的地址,那么就必须为它分配空间。这可能不是什么大问题,但是除非你明确地“希望”人们能够使用color的地址,否则你最好阻止它。 此外,当在类中声明常量字段时,它必须是static const(对于现代C++不是这样),并且不是所有编译器都支持静态常量成员的内联初始化。
This is a value of floor slope player is standing on (value ∈ [0; 90)°), if any. Because of engine limitations, it cannot be neither std::optional nor TOptional . I've came up with a solution to add another self explainable variable bIsOnFloor .
bool bIsOnFloor = false;
My C++ only internal setter for FloorSlope takes the following form:
Adding special case where FloorSlope parameter would take argument of -1.f would be hard to guess and not user friendly. Instead, I'd rather create Falseenum field:
enum { False };
This way, I can simply overload SetFloorSlope function that takes intuitive False instead of -1.f .
When a player character hits a floor upon applying gravity to it on tick, I simply call:
SetFloorSlope(FloorSlope);
… where FloorSlope is a float value ∈ [0; 90)°. Otherwise (if it does not hits a floor), I call:
SetFloorSlope(False);
This form (as opposed to passing -1.f ) is much more readable, and self explanatory.
Engine example
Another example may be to prevent or force initialization. Mentioned above Unreal Engine 4 commonly uses FHitResultstruct containing information about one hit of a trace, such as point of impact and surface normal at that point. This complex struct calls Init method by default, setting some values to certain member variables. This can be forced or prevented (public docs: FHitResult #constructor):
Passing NoInit to the constructor of FHitResult prevents initialization, what can lead to performance gain by not initializing values that will be initialized elsewhere.
Community example
FHitResult(NoInit) usage in DamirH's post on Comprehensive GameplayAbilities Analysis Series:
//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)
{
}
// (…)
8条答案
按热度按时间smdncfj31#
这就是declaring a compile-time integer constant的枚举技巧。它的优点是保证没有变量被示例化,因此没有运行时开销。无论如何,大多数编译器不会引入整型常量的开销。
cotxawn72#
枚举不占用任何空间,并且是不可变的。
如果你使用
const int color = 1;
,那么你就可以解决可变性问题,但是如果有人使用了color
(const int* p = &color;
)的地址,那么就必须为它分配空间。这可能不是什么大问题,但是除非你明确地“希望”人们能够使用color
的地址,否则你最好阻止它。此外,当在类中声明常量字段时,它必须是
static const
(对于现代C++不是这样),并且不是所有编译器都支持静态常量成员的内联初始化。**免责声明:**此答案不应被视为对所有数值常量使用
enum
的建议。您应该做您(或您的同事)认为更具可读性的事情。答案只是列出了一些您 * 可能 * 更喜欢使用enum
的原因。hgc7kmma3#
如果这是旧代码,那么枚举可能已经被用于“枚举黑客”。
你可以了解更多关于“枚举黑客”,例如,在这个链接:enum hack
简而言之:这允许定义一次值,就像#define或定义变量一样,但与#define不同-这里编译器确保值是一个数字(int,r-value)并防止你做各种各样的恶作剧,你可以做与预编译器的简单“搜索和替换”,并不像定义一个变量-从来没有占用空间下的任何编译器或配置,并防止对它进行更改(如果您足够努力,即使是const变量有时也可以更改)。
1tuwyuhd4#
color
是可变的(意外地)。无法更改
color
。enum
的另一个选项是,enum
和const int
提供了完全相同的概念;关于enum
保存空间的普遍看法,我认为没有与此相关的内存限制,编译器足够聪明,可以在需要时优化const int
。[Note:如果有人试图在
const int
上使用const_cast<>
;它将导致未定义的行为(这是不好的)。然而,同样的情况对enum
是不可能的。所以我个人最喜欢的是enum
]bnlyeluc5#
这种方法的一个用途是当你在做模板元编程的时候,因为枚举对象不是左值,而
static const
成员是。它也曾经是不允许你在类定义中初始化静态整型常量的编译器的一个常见的解决方案。这在another question中解释。kyvafyod6#
当您使用
enum {color = 1}
你不用任何记忆就像
#define color 1
如果你声明一个变量
那么你就占用了内存来存放一个不需要修改的值。
nuypyhwy7#
Answer
Readability and performance.
Details are describbed as notes to examples below.
Use cases
Personal example
In Unreal Engine 4 (C++ game engine), I have following property (engine exposed member variable):
This is a value of floor slope player is standing on (value ∈ [0; 90)°), if any.
Because of engine limitations, it cannot be neither
std::optional
norTOptional
.I've came up with a solution to add another self explainable variable
bIsOnFloor
.My C++ only internal setter for
FloorSlope
takes the following form:Adding special case where
FloorSlope
parameter would take argument of-1.f
would be hard to guess and not user friendly. Instead, I'd rather createFalse
enum
field:This way, I can simply overload
SetFloorSlope
function that takes intuitiveFalse
instead of-1.f
.When a player character hits a floor upon applying gravity to it on tick, I simply call:
… where
FloorSlope
is afloat
value ∈ [0; 90)°. Otherwise (if it does not hits a floor), I call:This form (as opposed to passing
-1.f
) is much more readable, and self explanatory.Engine example
Another example may be to prevent or force initialization. Mentioned above Unreal Engine 4 commonly uses
FHitResult
struct
containing information about one hit of a trace, such as point of impact and surface normal at that point.This complex
struct
callsInit
method by default, setting some values to certain member variables. This can be forced or prevented (public docs:FHitResult
#constructor):Epic Games defines such
enum
s similiar, but adds redundantenum
names:Passing
NoInit
to the constructor ofFHitResult
prevents initialization, what can lead to performance gain by not initializing values that will be initialized elsewhere.Community example
FHitResult(NoInit)
usage in DamirH's post on Comprehensive GameplayAbilities Analysis Series:tjrkku2a8#
我没有看到它被提及,另一个用途是限定常量的范围。我目前正在使用Visual Studio 2005编写代码,它现在被移植到android - g++。在VS 2005中,你可以有这样的代码
enum MyOpts { OPT1 = 1 };
,并将其用作MyOpts::OPT 1-编译器没有抱怨它,即使它是无效的。g++将这样的代码报告为错误,所以一个解决方案是使用匿名枚举,如下所示:struct MyOpts { enum {OPT1 =1}; };
,现在两个编译器都很满意。