2016-09-21 43 views
3

混乱していますか?私はあまりにも... は正常に動作し、コンストラクタのメンバー初期化リストにstd::mapを初期化し、上記次コンストラクタの初期化子リストのメンバでない初期化

typedef std::map<std::string , double> Thresholds; 

class Foo 
{ 
    public: 
     Foo(const double & _toxicThres , const double & _zeroThres) 
    : thresholds 
    (
     MapInitializer<std::string , double>() 
      .Add("toxic" , _toxicThres) 
      .Add("zero" , _zeroThres) 
    ) 

    private: 
     Thresholds thresholds; 
}; 

を考えてみましょう。今、このことを考慮してください。

typedef std::map<std::string , double> Thresholds; 
struct CommonData 
{ 
    Thresholds thresholds; 
}; 

class Foo //a mixin 
{ 
    public: 
     Foo(Thresholds & thresholds , const double & _toxicThres , const double & _zeroThres) 
    : thresholds 
    (
     MapInitializer<std::string , double>() 
      .Add("toxic" , _toxicThres) 
      .Add("zero" , _zeroThres) 
    ) 
}; 

class Bar //another mixin 
{ 
    public: 
     Bar(Thresholds & thresholds , const double & _warningThres , const double & _zeroThres) 
    : thresholds 
    (
     MapInitializer<std::string , double>() 
      .Add("warning" , _warningThres) 
      .Add("zero" , _zeroThres) 
    ) 
}; 

class OtherGasThreshold{/*...*/}; //yet another mixin, etc... 

template<typename ThresholdMixin> //Foo , Bar , or others ... 
class ThresholdSensor : public ThresholdMixin 
{ 
    public: 
     ThresholdSensor(double val1 , double val2) 
      : ThresholdMixin(cd.thresholds, val1 , val2) 
     {} 

    private: 
     CommonData cd; 
}; 

注意をMapIniializerコードがhereから来て、そして上記コンパイルされないでしょう。もちろん、

template<class K, class V> 
class MapInitializer 
{ 
    std::map<K,V> m; 
public: 
    operator std::map<K,V>() const 
    { 
     return m; 
    } 

    MapInitializer& Add(const K& k, const V& v) 
    { 
     m[ k ] = v; 
     return *this; 
    } 
}; 

ですが、一つにThresholdSensor::CommonDataでマップを初期化するためにどのような方法があることコンストラクタinit中のミックスインの数。つまりマップを参照して渡すことができますか?それをmixinsコンストラクタで初期化しますか?

+0

true true固定 – nass

+0

このコードでは 'MapInitializer'は完全に不要であることをご存知ですか? –

+0

@MooingDuckをもっと良く使うには、括弧付きの初期化子を使用してください。 –

答えて

1

を、thresholdsは、コンストラクタにパラメータとして渡されます。

初期化子の構文は、スーパークラスとクラスメンバーを初期化するためのものです。他のすべてのために、それがために、コンストラクタの体が何であるかです:

class Bar 
{ 
    public: 
     Bar(Thresholds & thresholds, 
      const double & _warningThres, 
      const double & _zeroThres) 
    { 
     thresholds=MapInitializer<std::string , double>() 
      .Add("warning" , _warningThres) 
      .Add("zero" , _zeroThres) 
    } 
}; 

は、この例にはスーパークラスまたは他のクラスのメンバーがありませんので、これは正常に動作します。あなたの例では、コンストラクタの初期化セクションでthresholdsを初期化する必要がある明示的な依存関係はありません。

しかし、あるとします。 thresholdsとスーパークラスまたはクラスメンバの間にある種の依存関係があったとし、ある種の依存関係のために他のオブジェクトを初期化する前にthresholdsを最初に初期化する必要があるとしましょう。クラスメンバ、またはスーパークラスは、コンストラクタの初期化セクションで初期化する必要があります。したがって、thresholdsも初期化する必要があります。

class Bar 
{ 
    public: 
     Bar(Thresholds & thresholds, 
      const double & _warningThres, 
      const double & _zeroThres); 

    class private_bar { 
    public: 
     private_bar(Thresholds &thresholds); 
    }; 

private: 
    private_bar secret; 
}; 

だがBarのコンストラクタのコンストラクタはsecretだけthresholdsを初期化した後、private_barに渡さ構築するために必要である」としましょう:私たちは具体的な例を使用している場合

それははるかに簡単です。これが起こる状況を想像するのは難しいことではありません。 private_barのコンストラクタは、初期化されたthresholdsを使用しています。今度は、Barのコンストラクタの初期化セクションでsecretメンバを初期化しましたが、これが発生する前にthresholdsを初期化する必要があります。私はこれがあなたの質問が沸き起こるものだと信じています。

ソリューションは、通常、次の一般的なデザインパターンをとる。このような状況で

class Bar 
{ 

     static Thresholds &init_thresholds(Thresholds &thresholds) 
     { 
      thresholds=MapInitializer<std::string , double>() 
      .Add("warning" , _warningThres) 
      .Add("zero" , _zeroThres) 

      return thresholds; 
     } 

    public: 
     Bar(Thresholds & thresholds, 
      const double & _warningThres, 
      const double & _zeroThres) 
      : secret(init_thresholds(thresholds)) 
     { 
     } 

    class private_bar { 
    public: 
     private_bar(Thresholds &thresholds); 
    }; 

private: 
    private_bar secret; 
}; 

そして、それはそれは、あなたの質問に答えるために、「クラスのコンストラクタリスト内の非会員の初期化を行うことは可能」だ方法です。それが本当に必要な場合は、まず何かをコンストラクタリストで初期化する必要があるからです。それ以外の場合は、メインのコンストラクタ本体で単純に初期化することができます。

しかし、コンストラクタリストで何かを初期化する必要がある場合は、ヘルパー関数を使用して非メンバオブジェクトを初期化するために、その構造の上に「ピギーバック」するだけです。

2

基本サブオブジェクトはデータメンバーの前に作成されるため、一般に基本初期化子は派生データメンバーを使用できません。私はちょうどそれをシンプルに保つため、通常のメンバ関数だろう

struct Foo 
{ 
    void Init(Thresholds & thresholds) 
    { 
     thresholds.emplace("foo", 1.0); 
     thresholds.emplace("bar", 1.5); 
    } 

    // ... 
}; 

template <typename Mx> 
struct Thing : Mx 
{ 
    Thresholds thresholds; 

    Thing() { Mx::Init(thresholds); } 
    //  ^^^^^^^^^^^^^^^^^^^^^ 

    // ... 
}; 

は使用法:問題のコンストラクタで

Thing<Foo> x; 
+1

または、ミックスインをInitializerに入力してから、メンバーを通常どおりに構築します。http ://coliru.stacked-crooked.com/a/0f3ae4d6d50adf18(+コピーを避けるために構築を移動) –

+1

@MooingDuck:悪いトリックではありませんが、私はまだそのようなコードを励ましているとは確信していません:-) –

+1

@MooingDuck:このアプローチは、「Thing():Thing({}){}」とプライベート 'Thing(Thresholds t):しきい値(static_cast (Mx :: Init )、t)){} ' –

関連する問題