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

时间:2016-09-21 00:35:41

标签: c++ c++11 initializer-list

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

    var result = bazzers
        .Select(bazzer => bazzer.Bar)
        .Distinct()
        .Select(bar => new
        {
            Bar = bar,
            LoyalFoos = bazzers
                .GroupBy(bazzer => bazzer.Foo)
                .Where(group => group.All(bazzer => bazzer.Bar == bar))
                .SelectMany(group => group)
        });

以上工作正常,并在构造函数的成员初始化列表中初始化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; }; 代码来自here,而且是

MapIniializer

当然上面的代码不能编译,但是有没有办法在构造函数init期间在其中一个mixin中的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; } }; 中初始化地图。即我可以通过引用传递地图,在mixins构造函数中初始化它吗?

2 个答案:

答案 0 :(得分: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 :(得分: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,就是这样。现在,您已在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 。否则,您只需在主构造函数体中初始化它。

但是如果需要在构造函数列表中初始化其他东西,那么你只需“搭载”在该构造之上,以便使用辅助函数初始化非成员对象。