46

何らかの定数値をクラスに関連付ける場合、同じ目的を達成する 2 つの方法があります。

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;

純粋なスタイルの問題以外に、一方が他方よりも好ましい理由はありますか?

4

6 に答える 6

63

多くのenumコンパイラが値のインプレース初期化をサポートしていなかったため、ハックが必要でした。これはもはや問題ではないため、他のオプションを選択してください。最新のコンパイラは、この定数を最適化して、記憶域を必要としないようにすることもできます。

バリアントを使用しない唯一の理由は、値のアドレスを取得することを禁止static constしたい場合です。定数のアドレスを取得できる一方で、値のアドレスを取得することはできません (これにより、コンパイラーがスペースを予約するように促されます)。結局のところ、そのアドレスが実際に取得された場合のみ)。enum

さらに、アドレスを取得すると、定数も明示的に定義されていない限り、リンク時エラーが発生します。宣言のサイトでまだ初期化できることに注意してください。

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

int const foo::bar; // Definition.
于 2008-10-15T14:46:35.353 に答える
11

それらは同一ではありません:

size_t *pLife1 = &Foo::Life;
size_t *pLife2 = &Bar::Life;
于 2008-10-15T14:46:19.957 に答える
9

1 つの違いは、たとえば、より適切な型チェックを取得するために、列挙型がメソッド パラメーターとして使用できる型を定義することです。どちらもコンパイラによってコンパイル時の定数として扱われるため、同じコードが生成されるはずです。

于 2008-10-15T14:50:18.527 に答える
7

static const値は、コードの 99% と同じようenumに r 値として扱われます。定数 r 値には、メモリが生成されることはありません。定数の利点enumは、他の 1% の左辺値にならないことです。static const値はタイプ セーフであり、float、c-string などを使用できます。

Foo::Lifeメモリが関連付けられている場合、コンパイラは左辺値を作成します。これを行う通常の方法は、そのアドレスを取得することです。例えば&Foo::Life;

GCC がアドレスを使用する微妙な例を次に示します。

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

コンパイラによって生成されたコードは、Lifeおよびのアドレスを使用しますEverythingFoo::Lifeさらに悪いことに、これはとのアドレスが見つからないというリンカ エラーを生成するだけFoo::Everythingです。この動作は完全に標準に準拠していますが、明らかに望ましくありません。これが発生するコンパイラ固有の方法は他にもあり、すべて標準に準拠しています。

適合する C++11 コンパイラがあれば、正しいコードは次のようになります。

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

これは常に左辺値であることが保証されており、タイプセーフであり、両方の長所を備えています。

于 2008-10-17T05:47:29.417 に答える
5

別の 3 番目の解決策はありますか?

微妙な違いの 1 つは、列挙型をヘッダーで定義する必要があり、すべてのユーザーに表示されることです。依存関係を回避している場合、これは苦痛です。たとえば、PImpl で列挙型を追加すると、やや非生産的になります。

// MyPImpl.hpp

class MyImpl ;

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

もう 1 つの3 番目の解決策は、質問で提案されている「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 の作成者は、PImpl の全体的な目的と同様に、MyPImpl ユーザーが再コンパイルする必要なく、必要に応じて「Life」の値を変更できます。

于 2008-10-15T22:30:01.173 に答える
5

必要に応じて、静的 const メンバー値のアドレスを取得できます。アドレスを取得するには、列挙型の別のメンバー変数を宣言する必要があります。

于 2008-10-15T14:47:32.807 に答える