构造函数初始化列表中的非成员初始化

Non-member initialization in constructor initializer list

困惑?我也是... 考虑以下

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 构造函数中初始化它吗?

基本子对象在数据成员之前构造,因此通常基本初始化程序不能使用派生数据成员。

我会保持简单并有一个普通的成员函数:

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;

在有问题的构造函数中,thresholds 作为参数传递给构造函数。

初始化语法用于初始化 superclasses 和 class 成员。对于其他所有内容,这就是构造函数的主体的用途:

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

本例中没有超级classes 或其他class 成员,因此可以正常工作。我在您的示例中没有看到任何需要在构造函数的初始化部分中初始化 thresholds 的显式依赖项。

但假设有。假设 thresholds 和 superclass 或 class 成员之间存在某种依赖关系,并且由于某些未指定的依赖关系,有必要初始化 thresholds 首先,在初始化另一个对象之前。 class 成员,或者 superclass 必须在构造函数的初始化部分初始化,所以我们也需要在那里初始化 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;
};

这就是 "possible to do non-member initialization in the constructor list of a class" 回答您问题的方式。如果确实有必要这样做,那一定是因为 else 需要首先在构造函数列表中初始化。否则你可以简单地在主构造函数体中初始化它。

但是如果需要在构造函数列表中初始化其他内容,那么您只需 "piggy-back" 在该构造之上,使用辅助函数初始化您的非成员对象。