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

Joh*_*ing 43 c++ class-design

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

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

class Bar
{
public:
    enum {Life = 42};
};
Run Code Online (Sandbox Code Playgroud)

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

size_t fooLife = Foo::Life;
size_t barLife = Bar::Life;
Run Code Online (Sandbox Code Playgroud)

除了纯粹的风格之外,还有什么理由可以解释为什么一个人比另一个人更可取?

Kon*_*lph 56

之前的enumhack是必要的,因为许多编译器不支持值的就地初始化.由于这不再是问题,请选择其他选项.现代编译器也能够优化此常量,因此不需要存储空间.

不使用static const变量的唯一原因是,如果您想禁止获取值的地址:您可以获取enum值的地址,同时可以获取常量的地址(这将提示编译器保留空间毕竟,这个价值,但只有它的地址真的被采取时).

此外,除非明确定义常量,否则获取地址将产生链接时错误.请注意,它仍然可以在声明的站点初始化:

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

int const foo::bar; // Definition.
Run Code Online (Sandbox Code Playgroud)

  • 如果你取"静态const"的地址但没有定义它,那么它应该导致链接器错误. (3认同)
  • 实际上 - 您的编译器不应为常量分配任何存储空间.该标准明确规定只有当对象被"使用"时,即.它需要的地址,是所需对象的定义.然后开发人员必须明确提供定义! (2认同)

小智 11

它们不相同:

size_t *pLife1 = &Foo::Life;
size_t *pLife2 = &Bar::Life;
Run Code Online (Sandbox Code Playgroud)

  • 基本上就是我所说的,用更少的话说.;-) (2认同)
  • 代码示例应该读取(添加`const`s):`const size_t*pLife1 =&Foo :: Life;``const size_t*pLife2 =&Bar :: Life;`第一个是好的,但是第二个产生编译器错误,因为枚举器列表中的标识符被声明为常量,并且您不能获取常量的地址. (2认同)

Mar*_*som 9

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


def*_*ode 7

static const值被视为r值,就像enum您将看到的99%的代码一样.常量r值永远不会为它们生成内存.优势enum常数是它们不能成为另外1%的l值.这些static const值是类型安全的,允许浮动,c字符串等.

如果编译器Foo::Life具有与之关联的内存,则编译器将生成l值.通常的方法是采取其地址.例如&Foo::Life;

以下是GCC将使用地址的一个微妙示例:

int foo = rand()? Foo::Life: Foo::Everthing;
Run Code Online (Sandbox Code Playgroud)

编译器生成的代码使用的地址LifeEverything.更糟的是,这仅产生约失踪的地址链接错误Foo::LifeFoo::Everything.这种行为完全符合标准,但显然不合需要.还有其他编译器特定方式可以实现,并且所有标准符合.

一旦你有一个符合要求的c ++ 11编译器,正确的代码将是

class Foo {
 public:
  constexpr size_t Life = 42;
};
Run Code Online (Sandbox Code Playgroud)

这保证始终是一个l值,它是类型安全的,两全其美.

  • 您的意思是“保证始终是 _rvalue_”吗?这比你实际写的更有可能,但仍然不能保证。如果使用了 `constexpr` 的地址,就可以为其分配存储空间,在这种情况下,它在初始化后就相当于一个 `const` 变量。但是可以肯定的是,如果您不这样做,那么`constexpr` 是无存储的,并且由于各种原因是最佳解决方案。 (2认同)
  • `constexpr size_t Life = 42;` 会导致编译错误。它也需要是静态的。这是根据 Clang 3.8。 (2认同)

Jam*_*ran 5

好吧,如果需要,你可以获取静态const成员值的地址.你必须声明一个枚举类型的单独成员变量来获取它的地址.


pae*_*bal 5

还有第三个解决方

一个微妙的区别是枚举必须在标题中定义,并且对所有人都可见.当你避免依赖时,这是一个痛苦.例如,在PImpl中,添加枚举有点适得其反:

// MyPImpl.hpp

class MyImpl ;

class MyPimpl
{
   public :
      enum { Life = 42 } ;
   private :
      MyImpl * myImpl ;
}
Run Code Online (Sandbox Code Playgroud)

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

// MyPImpl.hpp

class MyImpl ;

class MyPimpl
{
   public :
      static const int Life ;
   private :
      MyImpl * myImpl ;
}
Run Code Online (Sandbox Code Playgroud)

.

// MyPImpl.cpp
const int MyPImpl::Life = 42 ;
Run Code Online (Sandbox Code Playgroud)

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

这将使MyPimpl作者能够根据需要更改"Life"的值,而不需要重新编译MyPImpl用户,PImpl的总体目标也是如此.