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

时间:2023-02-11 22:49:04

Confused? Me too... Consider the following

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

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

The above works fine and initializes an std::map in the constructor's member initialisation list. Now consider this:

上面的工作正常,并在构造函数的成员初始化列表中初始化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;
};

Note that the MapIniializer code comes from here, and is

请注意,MapIniializer代码来自此处,并且是

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

Of course the above would not compile, but is there any way to init the map in ThresholdSensor::CommonData in one of the mixins during constructor init. ie can I pass by reference the map, init it in the mixins constructor?

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

2 个解决方案

#1


1  

In the constructor in question, thresholds gets passed as a parameter to the constructor.

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

The initializer syntax is for initializing superclasses, and class members. For everything else, that's what the constructor's body is for:

初始化语法用于初始化超类和类成员。对于其他一切,这就是构造函数的主体:

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

There are no superclasses or other class members in this example, so this works fine. I do not see any explicit dependency in your example that requires thresholds to be initialized in the initialization section of the constructor.

在这个例子中没有超类或其他类成员,所以这很好。我没有在您的示例中看到任何需要在构造函数的初始化部分初始化阈值的显式依赖项。

But suppose there is. Let's say there was a dependency of some sort between thresholds, and either the superclass, or a class member, and due to some unspecified dependency it becomes necessary to initialize thresholds first, before initializing the other object. The class member, or the superclass must be initialized in the initialization section of the constructor, so we need to initialize thresholds there, too.

但是假设有。假设在阈值和超类或类成员之间存在某种依赖关系,并且由于某些未指定的依赖关系,在初始化另一个对象之前,首先需要初始化阈值。必须在构造函数的初始化部分初始化类成员或超类,因此我们也需要初始化阈值。

It's much easier if we use a concrete example:

如果我们使用一个具体的例子,那就容易多了:

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

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

private:
    private_bar secret;
};

Let's say that Bar's constructor needs to construct secret but only after initializing thresholds, that gets passed to private_bar's constructor. It's not hard to imagine a situation where this happens. private_bar's constructor uses an initialized thresholds, and that's that. Now, you have initialize the secret member in the initialization section of Bar's constructor, but thresholds needs to be initialized before this happens. I believe that this is what your question boils down to.

假设Bar的构造函数需要构造秘密,但只有在初始化阈值之后才会传递给private_bar的构造函数。不难想象出现这种情况的情况。 private_bar的构造函数使用初始化的阈值,就是这样。现在,您已经在Bar的构造函数的初始化部分初始化了秘密成员,但是在此之前需要初始化阈值。我相信这就是你的问题归结为。

In this situation the solution typically takes the following generic design pattern:

在这种情况下,解决方案通常采用以下通用设计模式:

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

And that's how it's "possible to do non-member initialization in the constructor list of a class", to answer your question. If it is truly necessary to do that, it must be because something else needs to be initialized in the constructor list, first. Otherwise you can simply initialize it in the main constructor body.

这就是“可以在类的构造函数列表中进行非成员初始化”,以回答您的问题。如果真的有必要这样做,那一定是因为首先需要在构造函数列表中初始化其他东西。否则,您只需在主构造函数体中初始化它。

But if something else needs to be initialized in the constructor list, then you just "piggy-back" on top of that construction, in order to initialize your non-member object, using a helper function.

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

#2


2  

The base subobjects are constructed before the data members, so generally a base initializer can't use derived data members.

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

I would just keep it simple and have a normal member function:

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

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); }
    //        ^^^^^^^^^^^^^^^^^^^^^

    // ...
};

Usage:

用法:

Thing<Foo> x;

#1


1  

In the constructor in question, thresholds gets passed as a parameter to the constructor.

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

The initializer syntax is for initializing superclasses, and class members. For everything else, that's what the constructor's body is for:

初始化语法用于初始化超类和类成员。对于其他一切,这就是构造函数的主体:

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

There are no superclasses or other class members in this example, so this works fine. I do not see any explicit dependency in your example that requires thresholds to be initialized in the initialization section of the constructor.

在这个例子中没有超类或其他类成员,所以这很好。我没有在您的示例中看到任何需要在构造函数的初始化部分初始化阈值的显式依赖项。

But suppose there is. Let's say there was a dependency of some sort between thresholds, and either the superclass, or a class member, and due to some unspecified dependency it becomes necessary to initialize thresholds first, before initializing the other object. The class member, or the superclass must be initialized in the initialization section of the constructor, so we need to initialize thresholds there, too.

但是假设有。假设在阈值和超类或类成员之间存在某种依赖关系,并且由于某些未指定的依赖关系,在初始化另一个对象之前,首先需要初始化阈值。必须在构造函数的初始化部分初始化类成员或超类,因此我们也需要初始化阈值。

It's much easier if we use a concrete example:

如果我们使用一个具体的例子,那就容易多了:

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

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

private:
    private_bar secret;
};

Let's say that Bar's constructor needs to construct secret but only after initializing thresholds, that gets passed to private_bar's constructor. It's not hard to imagine a situation where this happens. private_bar's constructor uses an initialized thresholds, and that's that. Now, you have initialize the secret member in the initialization section of Bar's constructor, but thresholds needs to be initialized before this happens. I believe that this is what your question boils down to.

假设Bar的构造函数需要构造秘密,但只有在初始化阈值之后才会传递给private_bar的构造函数。不难想象出现这种情况的情况。 private_bar的构造函数使用初始化的阈值,就是这样。现在,您已经在Bar的构造函数的初始化部分初始化了秘密成员,但是在此之前需要初始化阈值。我相信这就是你的问题归结为。

In this situation the solution typically takes the following generic design pattern:

在这种情况下,解决方案通常采用以下通用设计模式:

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

And that's how it's "possible to do non-member initialization in the constructor list of a class", to answer your question. If it is truly necessary to do that, it must be because something else needs to be initialized in the constructor list, first. Otherwise you can simply initialize it in the main constructor body.

这就是“可以在类的构造函数列表中进行非成员初始化”,以回答您的问题。如果真的有必要这样做,那一定是因为首先需要在构造函数列表中初始化其他东西。否则,您只需在主构造函数体中初始化它。

But if something else needs to be initialized in the constructor list, then you just "piggy-back" on top of that construction, in order to initialize your non-member object, using a helper function.

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

#2


2  

The base subobjects are constructed before the data members, so generally a base initializer can't use derived data members.

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

I would just keep it simple and have a normal member function:

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

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); }
    //        ^^^^^^^^^^^^^^^^^^^^^

    // ...
};

Usage:

用法:

Thing<Foo> x;