2016-09-21 4 views
3

Confused? Я тоже ... Рассмотрим следующийИнициализация нечлена в списке инициализаторов конструктора

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; 
}; 

выше работает отлично и инициализирует std::map в списке член инициализации конструктора. Теперь рассмотрим это:

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 в одном миксинов во время инициализации конструктора. т.е. могу ли я передать ссылку на карту, инициализировать ее в конструкторе mixins?

+0

true true fixed – nass

+0

Знаете ли вы, что «MapInitializer» совершенно не нужен для этого кода? Просто используйте 'initializer_list' –

+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 «s конструктор должен построить secret, но только после инициализации thresholds, который получает передается private_bar» s конструктор. Нетрудно представить ситуацию, когда это происходит. Конструктор private_bar использует инициализированный thresholds, и все. Теперь вы инициализируете член secret в разделе инициализации конструктора Bar, но 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; 
}; 

И вот как это «можно сделать инициализацию, не являющемуся членом в списке конструктора класса», чтобы ответить на ваш вопрос. Если это действительно необходимо сделать, это должно быть потому, что сначала необходимо сначала инициализировать в списке конструкторов else. В противном случае вы можете просто инициализировать его в основном корпусе конструктора.

Но если что-то еще нужно инициализировать в списке конструкторов, тогда вы просто «копируете» поверх этой конструкции, чтобы инициализировать свой объект, не являющийся членом, используя вспомогательную функцию.

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

Или, если миксины заполняют инициализатор, а затем создайте элемент как обычно: http : //coliru.stacked-crooked.com/a/0f3ae4d6d50adf18 (+ перемещение конструкции во избежание любых копий) –

+1

@MooingDuck: Неплохой трюк, хотя я еще не совсем уверен, что я бы поощрил такой код :-) –

+1

@MooingDuck: Этот подход может быть применен непосредственно к моему коду: 'Thing(): Thing ({}) {}' и private 'Thing (Thresholds t): thresholds (static_cast (Mx :: Init (t), t)) {} ' –

Смежные вопросы