【问题标题】:Non-member initialization in constructor initializer list构造函数初始化器列表中的非成员初始化
【发布时间】:2016-09-21 00:35:41
【问题描述】:

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

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

当然,上面的代码不会编译,但是有没有办法在构造函数初始化期间在其中一个 mixin 中初始化 ThresholdSensor::CommonData 中的映射。即我可以通过引用传递地图,在mixins构造函数中初始化它吗?

【问题讨论】:

  • 真正真正的固定
  • 你知道MapInitializer对于这段代码是完全没有必要的吗?只需使用initializer_list
  • @MooingDuck 更好,使用大括号初始化器
  • @M.M:(在封面下使用initializer_list
  • @M.M 你能详细说明一下吗?我之前尝试过 `Foo(...):thresholds{ {"toxic",_toxicThres} , { "zero", _zeroThres } } 但它没有用。什么是正确的语法

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


【解决方案1】:

基础子对象在数据成员之前构造,所以通常基础初始化器不能使用派生数据成员。

我会保持简单并拥有一个正常的成员函数:

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;

【讨论】:

  • 或者,让 mixins 填充一个 Initializer,然后正常构造成员:coliru.stacked-crooked.com/a/0f3ae4d6d50adf18(+ 移动构造以避免任何副本)
  • @MooingDuck:不错的把戏,虽然我还不完全相信我会鼓励这样的代码:-)
  • @MooingDuck:这种方法甚至可以直接应用于我的代码:Thing() : Thing({}) {} 和私有 Thing(Thresholds t) : thresholds(static_cast&lt;Thresholds&amp;&amp;&gt;(Mx::Init(t), t)) {}
【解决方案2】:

在有问题的构造函数中,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;
};

这就是“可以在类的构造函数列表中进行非成员初始化”的方式来回答您的问题。如果确实有必要这样做,那一定是因为 else 需要首先在构造函数列表中初始化。否则,您可以简单地在主构造函数主体中对其进行初始化。

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

【讨论】:

    猜你喜欢
    • 2011-05-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多