2012-07-03 22 views
7

私はちょうどいくつかのプリミティブ型であるC++で新しい型を定義したいと思います(私の例ではint、どんな型でも可能です)。この例では、タイプNodeIdと呼んでいます。パフォーマンス:typedefとプリミティブ型のラッパークラス?

私はちょうどtypedef int NodeIdを使用できます。 NodeIdのデフォルト値が必要なので、#define NULL_NODE_ID -1を使用します。

class NodeId 
{ 
    int value; 
public: 
    inline NodeId() : value(-1) {} 
    inline NodeId(int value) : value(value) {} 
    inline operator int() {return value;} 
    inline bool isValid() {return value != -1;} 
    //... 
}; 

を使用しての結果、パフォーマンスの欠点があります。

は今、私は機能isValid()とデフォルトコンストラクタがnull NodeIdを構築できるようにする代わりにtypedefのクラスを定義するよりよいことだと思います2番目の方法?

+0

新しいコンパイラを使用する場合は、intを使用するのと同じにする必要があります。 – mfontanini

+0

@mfontaniniなぜあなたは質問をdownvoteでしたか?これは質問への答えです。そのように投稿するべきです。 – leemes

+0

@leemesなぜあなたはそれが私であると仮定していますか? – mfontanini

答えて

6

実は、これはおそらく遅くなる可能性があり、二つの理由があります。

最初に、初期化されていないNodeIdを作成する方法はありません。通常、それは良いものです。しかし、あなたは次のようなコードを持っていると想像してください。

NodeId nodeid; 
foo.initializeNodeId(&nodeid); 

あなたは実際には必要ない余分な割り当てを行います。

特殊なコンストラクタを追加することで修正できます。 Foo :: initializeNodeId(& NodeId)を必要としないように、Foo :: createNodeId()を作成する方がはるかに優れていますが、Fooの定義を制御していない場合は不可能です。

第2に、NodeIdはコンパイル時定数式ではありません。 dasblinkenlightが示唆しているように、これはコードがパフォーマンス上の問題を引き起こすよりも合法ではないという問題を引き起こす可能性がはるかに高いですが、どちらも可能です。なぜなら、コンパイル時に実行された可能性のある実行時に、intを使用していた場合、コンパイラにコードを挿入して強制的に実行させるかもしれないので、これはNodeIdというクラスの問題ではないでしょう... )

C++ 11を使用している場合は、constexprで修正できます。また、コードを合法的なC++ 03にしたい場合は、マクロでそれを処理できます。

また、dasblinkenlightによって指摘されているように、2つの方法でconstが欠落しています。

最後に、クラス定義内で定義されたメソッドに "inline"を書く必要はありません。彼らはすでに本質的にインラインです。

はすべて一緒にそれを置く:

#if __cplusplus > 201000L 
#define CONSTEXPR_ constexpr 
#else 
#define CONSTEXPR_ 
#endif 

class NodeId 
{ 
    int value; 
public: 
    struct Uninitialized {}; 
    CONSTEXPR_ NodeId() : value(-1) {} 
    CONSTEXPR_ NodeId(Uninitialized) {}  
    CONSTEXPR_ NodeId(int value) : value(value) {} 
    CONSTEXPR_ operator int() const {return value;} 
    CONSTEXPR_ bool isValid() const {return value != -1;} 
    //... 
}; 

今、あなたは余分-1割り当てのコストを避けるために、これを行うことができます。

NodeId nodeId(NodeId::Uninitialized()); 
foo.initializeNodeId(&nodeid); 

そして、これは、合法的に非型テンプレートパラメータとしてNODEIDを使用する:

myClassTemplate<NodeId(3)> c; 

それとも、これは、コンパイラが合法的にわずか4にXを初期化することができますしてくださいする:

int x = 3; 
x += NodeId(1); 
+0

その偉大な答えをありがとう:) – Misch

+0

abarnert、箱から整数演算をサポートする可能性はありますか? 'NodeIdノード。ノード++; 'はコンパイルされません。私は 'operator ++'を手動で実装する必要があります。これらの簡単な演算子を手作業で実装しなくても可能ですか? – leemes

+0

@leemes:いいえ、完全に自動ではありません。しかし、boost.operatorは明示的にいくつかを定義し、他のものを無料で入手するのに役立ちます。これらのクラスをたくさん使っているなら、CRTP、基本クラス、コードジェネレーターを使うことができます。少なくとも、すべての演算子を一度定義すればよいのです。または、単一の "SmartInt"クラステンプレートを定義し、 "struct NodeIdTag {}; typedef SmartInt NodeId;" (もちろんマクロでまとめることができます)。 – abarnert

4

比較的新しいコンパイラを使用する場合、生成されるコードは同じにする必要があります。コンパイラは、これらのメンバ関数をインライン展開することに問題はありません。本当に疑問がある場合は、いつでも実行可能ファイルを逆アセンブルすることができます。

+0

それは本当ではありません。 'int'はPOD型ですが、' NodeId'はありません。コンパイラは、これが使われる場所で追加の初期化コードを生成します.Unionで使用することは禁じられており、他のクラスにPOD以外のものを「感染させる」でしょう。 –

+0

'NodeId x;'は、 'int x(-1);"と同じコードを生成するはずです。しかし、非POD引数は真です。 – mfontanini

+0

@DanHulme C++ 11 [許可する](Unsigned)(Unsigned)(Wikipedia.org/wiki/C%2B%2B11#Unrestricted_unions)集合内の非集計 – Praetorian

3

コンパイラの最適化設定によって、コンパイラはすべての行をインライン化できますが、パフォーマンスの違いはありません。私が目にすることができる唯一の欠点は、このクラスのオブジェクトに基づく式が、もはやテンプレートプログラミングで重要なコンパイル時定数としての資格を失うことです。それ以外の場合、ランタイムコストをかけずに明快さを増します。


P.S.あなたの operator intisValidconstが欠落しています

inline operator int() const {return value;} 
inline bool isValid() const {return value != -1;} 
+1

"コンパイル時*定数式*"、おそらく? –

+1

あなたはconstに関して正しいです、私はちょうどここにそれを掲示するためにこの例をハッキングしました。 – Misch

1

他のプログラミング言語(これをC++ DLLに書き込んでいるようなもの)で使用される可能性のあるライブラリにする場合は、typedef int

たとえば、APIのPythonラッパーまたはJavaラッパーを作成しているときに、クラスとタイプを移植することに苦労することはありません。あなたが心配しなければならないのは、intのビットサイズです。

関連する問題