静态const成员值与成员枚举:哪种方法更好?为什么?

如果要将某个常量值与类相关联,可以通过以下两种方法来实现相同的目标:

class Foo
{
public:
    static const size_t Life = 42;
};

class Bar
{
public:
    enum {Life = 42};
};

从客户的角度来看,它们在语法和语义上似乎是相同的:

size_t fooLife = Foo::Life;
size_t barLife = Bar::Life;

除了纯粹的风格之外,还有其他原因会导致一个人比另一个人更好吗?

John Dibling asked 2020-07-31T10:19:08Z
7个解决方案
56 votes

由于许多编译器不支持该值的就地初始化,因此29906008763200920097280黑客曾经是必需的。 由于这不再是问题,请选择其他方法。 现代编译器还能够优化此常数,因此不需要任何存储空间。

不使用static const变体的唯一原因是,如果您想禁止取值的地址:您不能取enum值的地址,而可以取常量的地址(这将提示编译器执行以下操作: 毕竟要为该值保留空间,但前提是确实要使用它的地址)。

另外,除非也明确定义该常数,否则采用地址将产生链接时错误。 注意,它仍然可以在声明的位置进行初始化:

struct foo {
    static int const bar = 42; // Declaration, initialization.
};

int const foo::bar; // Definition.
Konrad Rudolph answered 2020-07-31T10:30:17Z
11 votes

它们不相同:

size_t *pLife1 = &Foo::Life;
size_t *pLife2 = &Bar::Life;
answered 2020-07-31T10:30:39Z
9 votes

一个区别是,枚举定义了可以用作方法参数的类型,例如,以获得更好的类型检查。 两者都被编译器视为编译时间常数,因此它们应生成相同的代码。

Mark Ransom answered 2020-07-31T10:31:00Z
7 votes

Life值将被视为r值,就像您将在99%的代码中看到的Everything一样。 恒定的r值永远不会为其生成内存。 Foo::Life常数的优点是,它们不能成为其他1%的l值。 Foo::Everything值是类型安全的,并且允许浮点,C字符串等。

如果编译器具有关联的内存,则它将使Life为L值。 这样做的通常方法是使用其地址。 例如 Everything

这是一个微妙的示例,其中GCC将使用该地址:

int foo = rand()? Foo::Life: Foo::Everthing;

编译器生成的代码使用的地址为LifeEverything。更糟糕的是,这仅会生成有关Foo::LifeFoo::Everything缺少地址的链接器错误。此行为完全符合标准,尽管显然是不希望的。 还有其他特定于编译器的方式可以做到这一点,并且符合所有标准。

一旦有了合格的c ++ 11编译器,正确的代码将是

class Foo {
 public:
  constexpr size_t Life = 42;
};

这保证始终是一个l值,并且是类型安全的,两全其美。

deft_code answered 2020-07-31T10:31:43Z
5 votes

好吧,如果需要,您可以使用静态const成员值的地址。 您必须声明一个单独的枚举类型的成员变量以获取其地址。

James Curran answered 2020-07-31T10:32:03Z
5 votes

另一个第三种解决方案?

一个细微的区别是,枚举必须在标头中定义,并且对所有人可见。 当您避免依赖时,这很痛苦。 例如,在PImpl中,添加枚举会适得其反:

// MyPImpl.hpp

class MyImpl ;

class MyPimpl
{
   public :
      enum { Life = 42 } ;
   private :
      MyImpl * myImpl ;
}

另一个第三种解决方案是对问题中提出的“ const static”替代方案的一种变体:在标头中声明变量,但在源中定义变量:

// MyPImpl.hpp

class MyImpl ;

class MyPimpl
{
   public :
      static const int Life ;
   private :
      MyImpl * myImpl ;
}

.

// MyPImpl.cpp
const int MyPImpl::Life = 42 ;

请注意,MyPImpl :: Life的值对MyPImpl(包含MyPImpl.hpp)的用户是隐藏的。

这将使MyPimpl作者可以根据需要更改“ Life”的值,而无需MyPImpl用户重新编译,这是PImpl的总体目标。

paercebal answered 2020-07-31T10:32:45Z
3 votes

枚举黑客值得了解的原因有很多。 首先,枚举黑客在某种程度上表现得比const更像#define,有时这就是您想要的。 例如,采用const的地址是合法的,但是采用枚举的地址是不合法的,并且采用#define的地址通常也不合法。 如果您不想让人们得到您的整数常量的指针或引用,则枚举是强制执行此约束的好方法。 (有关通过编码决策强制执行设计约束的更多信息,请参阅条款18。)尽管好的编译器不会为整数类型的const对象预留存储空间(除非您创建指针或对该对象的引用),但草率的编译器可能会: 并且您可能不愿意为此类对象预留存储空间。 像#defines一样,枚举绝不会导致这种不必要的内存分配。

有效的C ++书

Amr Adel answered 2020-07-31T10:33:11Z
translate from https://stackoverflow.com:/questions/204983/static-const-member-value-vs-member-enum-which-method-is-better-why