const 字段的复杂初始化

发布于 2024-09-09 06:14:34 字数 798 浏览 2 评论 0原文

考虑这样一个类:

class MyReferenceClass
{
public:
    MyReferenceClass();
    const double ImportantConstant1;
    const double ImportantConstant2;
    const double ImportantConstant3;
private:
    void ComputeImportantConstants(double *out_const1, double *out_const2, double *out_const3);
}

有一个例程 (ComputeImportantConstants) 在运行时计算三个常量。假设计算相当复杂,并且本质上会同时产生所有三个值。此外,结果取决于构建配置,因此不能选择对结果进行硬编码。

是否有一种明智的方法将这些计算值存储在类的相应 const double 字段中?

如果没有,你能建议一种更自然的方式在 C++ 中声明这样的类吗?

在 C# 中,我将在这里使用带有静态构造函数的静态类,但这在 C++ 中不是一个选项。我也考虑过将ImportantConstant1..3设为非常量字段或函数调用,但两者似乎都较差。

我发现初始化 const 字段的唯一方法是 使用初始化列表,但似乎不可能在这样的列表中传递多输出计算的结果。

Consider a class like this one:

class MyReferenceClass
{
public:
    MyReferenceClass();
    const double ImportantConstant1;
    const double ImportantConstant2;
    const double ImportantConstant3;
private:
    void ComputeImportantConstants(double *out_const1, double *out_const2, double *out_const3);
}

There is a routine (ComputeImportantConstants) that computes three constants at runtime. Suppose the computation is fairly complex, and inherently produces all three values at once. Moreover, the results depend on build configuration, so hardcoding the results isn't an option.

Is there a sensible way to store these computed values in the corresponding const double fields of the class?

If not, can you suggest a more natural way to declare such a class in C++?

In C# I would use a static class with a static constructor here, but that isn't an option in C++. I have also considered making ImportantConstant1..3 either non-const fields or function calls, but both seem inferior.

The only way to initialize const fields that I found is to use initializer lists, but it doesn't seem possible to pass the results of a multi-output computation in such a list.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

沉默的熊 2024-09-16 06:14:34

为什么你不能这样做:

MyReferenceClass ComputeImportantConstants(){
    //stuff to compute
    return MyReferenceClass( const1, const2, const3 );
}

MyReferenceClass{
public:
    MyReferenceClass(double _1, double _2, double _3) 
        : m_Const1(_1),
        m_Const2(_2),
        m_Const3(_3){}

    double getImportantConst1() const { return m_Const1; }
    double getImportantConst2() const { return m_Const2; }
    double getImportantConst3() const { return m_Const3; }
private:
    const double m_Const1,
                 m_Const2,
                 m_Const3;
};

像那样,让计算函数变成工厂函数?

Why can't you do:

MyReferenceClass ComputeImportantConstants(){
    //stuff to compute
    return MyReferenceClass( const1, const2, const3 );
}

MyReferenceClass{
public:
    MyReferenceClass(double _1, double _2, double _3) 
        : m_Const1(_1),
        m_Const2(_2),
        m_Const3(_3){}

    double getImportantConst1() const { return m_Const1; }
    double getImportantConst2() const { return m_Const2; }
    double getImportantConst3() const { return m_Const3; }
private:
    const double m_Const1,
                 m_Const2,
                 m_Const3;
};

Like that and have the calculate function turn into a factory function?

舞袖。长 2024-09-16 06:14:34

首先 - 你可以做坏事:在 ComputeImportantConstants() 中丢弃 const 并将值放在那里。但不要这样做,因为这样你就对编译器撒了谎,它会试图找到最恶劣的方式来偿还。

第二:

做这样的事情:

class A
private:
  double important1;
  double important2;
  double important3;
  A() { ComputeImportantConstants(); } //no need for parameters, it accesses the members
  void ComputeImportantConstants();
public:
  inline double GetImportant1() { return important1; }
  inline double GetImportant2() { return important2; }
  inline double GetImportant3() { return important3; }
};

您仍然可以通过使其成为某种单例左右来改进此类(因为您希望计算只完成一次)。

first - you can do evil: cast away const in ComputeImportantConstants() and place the values there. Don't do it though, because then you lie to the compiler and it'll try to find the nastiest way to pay back.

second:

do something like this:

class A
private:
  double important1;
  double important2;
  double important3;
  A() { ComputeImportantConstants(); } //no need for parameters, it accesses the members
  void ComputeImportantConstants();
public:
  inline double GetImportant1() { return important1; }
  inline double GetImportant2() { return important2; }
  inline double GetImportant3() { return important3; }
};

you still can improve this class by making it some kind of singleton or so (since you want the computation to be done only once).

梦初启 2024-09-16 06:14:34

您可以将 const 字段移至基类,然后传递包装类来初始化它们:

class MyBase
{
protected:
    const double ImportantConstant1;
    const double ImportantConstant2;
    const double ImportantConstant3;

    struct Initializer
    {
        double d1;
        double d2;
        double d3;
    };

    MyBase(Initializer const& i):
        ImportantConstant1(i.d1),ImportantConstant2(i.d2),ImportantConstant3(i.d3)
    {}
};

class MyReferenceClass:
    private MyBase
{
public:
    using MyBase::ImportantConstant1;
    using MyBase::ImportantConstant2;
    using MyBase::ImportantConstant3;
    MyReferenceClass():
        MyBase(makeInitializer())
    {}

private:
    MyBase::Initializer makeInitializer()
    {
        MyBase::Initializer i;
        ComputeImportantConstants(&i.d1,&i.d2,&i.d3);
        return i;
    }

    void ComputeImportantConstants(double *out_const1, double *out_const2, double *out_const3);
};

You could move the const fields to a base class, and then pass an wrapper class to initialize them:

class MyBase
{
protected:
    const double ImportantConstant1;
    const double ImportantConstant2;
    const double ImportantConstant3;

    struct Initializer
    {
        double d1;
        double d2;
        double d3;
    };

    MyBase(Initializer const& i):
        ImportantConstant1(i.d1),ImportantConstant2(i.d2),ImportantConstant3(i.d3)
    {}
};

class MyReferenceClass:
    private MyBase
{
public:
    using MyBase::ImportantConstant1;
    using MyBase::ImportantConstant2;
    using MyBase::ImportantConstant3;
    MyReferenceClass():
        MyBase(makeInitializer())
    {}

private:
    MyBase::Initializer makeInitializer()
    {
        MyBase::Initializer i;
        ComputeImportantConstants(&i.d1,&i.d2,&i.d3);
        return i;
    }

    void ComputeImportantConstants(double *out_const1, double *out_const2, double *out_const3);
};
欲拥i 2024-09-16 06:14:34

我发现初始化常量字段的唯一方法是使用初始化列表,但似乎不可能在这样的列表中传递多输出计算的结果。

这是真的;但是,您可以初始化单个成员 - 这是一个常量结构。见下文。

我也考虑过将ImportantConstant1..3设为非常量字段或函数调用,但两者似乎都较差。

我不认为 getter 函数会较差。编译器很可能会内联它们。考虑一下:

class MyReferenceClass
{
public:
    MyReferenceClass() : m_constants( ComputeImportantConstants() ) { }

    inline double ImportantConstant1() const { return m_constants.c1; }
    inline double ImportantConstant2() const { return m_constants.c2; }
    inline double ImportantConstant3() const { return m_constants.c3; }

private:
    struct Constants {
        Constants( double c1_, double c2_, double c3_ ) : c1( c1_ ), c2( c2_ ), c3( c3_ ) { }

        const double c1;
        const double c2;
        const double c3;
    };

    Constants ComputeImportantConstants() {
        return Constants( 1.0, 2.0, 3.0 );
    }

    const Constants m_constants;
};

由于 m_constants 及其所有字段都是常量,因此其他成员方法无法更改这些值 - 只能在您在问题中概述的代码中更改。初始化可以是
之所以在这里使用,是因为我们初始化了一个值:一个结构体。

对常量的访问(很可能)与以前一样高效:考虑到 getter 的大小,建议内联函数,并且编译器很可能会这样做。

The only way to initialize const fields that I found is to use initializer lists, but it doesn't seem possible to pass the results of a multi-output computation in such a list.

That's true; however, you could initialize a single member - which is a struct of constants. See below.

I have also considered making ImportantConstant1..3 either non-const fields or function calls, but both seem inferior.

I don't think that getter functions would be inferior. The compiler would most likely inline them. Consider this:

class MyReferenceClass
{
public:
    MyReferenceClass() : m_constants( ComputeImportantConstants() ) { }

    inline double ImportantConstant1() const { return m_constants.c1; }
    inline double ImportantConstant2() const { return m_constants.c2; }
    inline double ImportantConstant3() const { return m_constants.c3; }

private:
    struct Constants {
        Constants( double c1_, double c2_, double c3_ ) : c1( c1_ ), c2( c2_ ), c3( c3_ ) { }

        const double c1;
        const double c2;
        const double c3;
    };

    Constants ComputeImportantConstants() {
        return Constants( 1.0, 2.0, 3.0 );
    }

    const Constants m_constants;
};

Since m_constants as well as all its fields are constant, the values cannot be changed by other member methods - just in the code you sketched in your question. An initialize can be
used here since we initialize a single value: a struct.

Access to the constants is (most likely) to be as efficient as before: the suggest to inline the functions and the compiler is quite likely to do so given how small the getters are.

没有你我更好 2024-09-16 06:14:34

要修改已接受的答案,请注意,从 C++11 开始,您可以执行非常巧妙的技巧。例如,您的原始问题可以通过 lambda 和构造委托来解决,如下所示:

class MyReferenceClass {

public: /* Methods: */

    MyReferenceClass()
        : MyReferenceClass([](){
                std::array<double, 3u> cs; /* Helper class, array or tuple */
                computeImportantConstants(&cs[0u], &cs[1u], &cs[2u]);
                return cs;
            })
    {}

    const double importantConstant1;
    const double importantConstant2;
    const double importantConstant3;

private: /* Methods: */

    MyReferenceClass(std::array<double, 3u> constants)
        : ImportantConstant1(constants[0u])
        , ImportantConstant2(constants[1u])
        , ImportantConstant3(constants[2u])
    {}

    static void computeImportantConstants(double * out_const1,
                                          double * out_const2,
                                          double * out_const3);

}; /* class MyReferenceClass { */

或者更好的是,如果可能的话,将初始化代码从computeImportantConstants 移动到构造函数中的 lambda 中。

实际上,使用 lambda 调用来初始化常量成员是一个非常方便的技巧,特别是因为您还可以将参数绑定和/或传递给 lambda。使用构造委托有助于简化成员的初始化,这些成员最好一起初始化或可能相互依赖。

但是,在使用构造委托时要格外小心,因为函数调用(或构造函数调用)的函数参数的初始化顺序是未定义的,并且最终可能会以错误的顺序或可能导致以下情况的方式初始化事物:如果出现故障或引发异常,则资源泄漏。

To amend the accepted answer, please note, that as of C++11 you can do very neat tricks. For example, your original problem can be solved with a lambda and construction delegation as follows:

class MyReferenceClass {

public: /* Methods: */

    MyReferenceClass()
        : MyReferenceClass([](){
                std::array<double, 3u> cs; /* Helper class, array or tuple */
                computeImportantConstants(&cs[0u], &cs[1u], &cs[2u]);
                return cs;
            })
    {}

    const double importantConstant1;
    const double importantConstant2;
    const double importantConstant3;

private: /* Methods: */

    MyReferenceClass(std::array<double, 3u> constants)
        : ImportantConstant1(constants[0u])
        , ImportantConstant2(constants[1u])
        , ImportantConstant3(constants[2u])
    {}

    static void computeImportantConstants(double * out_const1,
                                          double * out_const2,
                                          double * out_const3);

}; /* class MyReferenceClass { */

Or better yet, move the initialization code from computeImportantConstants into the lambda in the constructor, if possible.

In practice, using lambda calls to initialize constant members is a very handy trick, especially because you can also bind and/or pass arguments to the lambda. And using construction delegation helps to ease initialization of members which can best be initialized together or might depend on each another.

However, exercise extra caution when using construction delegation, because the initialization order of function arguments for a function call (or a constructor call) is undefined, and one might end up initializing things in an incorrect order, or in a manner which could lead to resource leaks if something fails or throws an exception.

迟月 2024-09-16 06:14:34

只需将事物分为易于初始化的部分和复杂的部分,并通过复制构造函数初始化复杂的部分:

// here's the part with the consts: 
struct ComplexPart
{
    const double a,b,c; 
    ComplexPart(double _a, double _b, double _c) {}
};
// here's the expensive calc function:
void calc(double *a,double *b,double *c);

// and this is a helper which returns an initialized ComplexPart from the computation:
ComplexPart calc2()
{
    double *a,*b,*c;
    calc(&a,&b,&b);
    return ComplexPart(a,b,c);
}
// put everything together:    
struct MyReferenceClass : public ComplexPart
{
    MyReferenceClass() : ComplexPart(calc2()) {}
};

Just split up the thing into the part that is simple to initialize and the complex part, and initialize the complex part via copy constructor:

// here's the part with the consts: 
struct ComplexPart
{
    const double a,b,c; 
    ComplexPart(double _a, double _b, double _c) {}
};
// here's the expensive calc function:
void calc(double *a,double *b,double *c);

// and this is a helper which returns an initialized ComplexPart from the computation:
ComplexPart calc2()
{
    double *a,*b,*c;
    calc(&a,&b,&b);
    return ComplexPart(a,b,c);
}
// put everything together:    
struct MyReferenceClass : public ComplexPart
{
    MyReferenceClass() : ComplexPart(calc2()) {}
};
要走就滚别墨迹 2024-09-16 06:14:34

类似这样的事情怎么样:

class A
{
  private:
    static void calc(double &d1, double &d2, double &d3)
    {
      d1 = 1.0;
      d2 = 2.0;
      d3 = 3.0;
    }
    class D
    {
      public:
        operator double() const
        {
          return(x);
        }
      private:
        friend class A;
        double x;
    };
  public:
    A()
    {
      calc(d1.x, d2.x, d3.x);
    }
    D d1, d2, d3;
};

#include <iostream>

int main()
{
  A a;
  std::cout << a.d1 << std::endl;
  std::cout << a.d2 << std::endl;
  std::cout << a.d3 << std::endl;
  // the following lines will not compile so you can't change the value
  // std::cout << a.d3.x << std::endl;
  // a.d2.x = 0.0;
  return(0);
}

What about something like that:

class A
{
  private:
    static void calc(double &d1, double &d2, double &d3)
    {
      d1 = 1.0;
      d2 = 2.0;
      d3 = 3.0;
    }
    class D
    {
      public:
        operator double() const
        {
          return(x);
        }
      private:
        friend class A;
        double x;
    };
  public:
    A()
    {
      calc(d1.x, d2.x, d3.x);
    }
    D d1, d2, d3;
};

#include <iostream>

int main()
{
  A a;
  std::cout << a.d1 << std::endl;
  std::cout << a.d2 << std::endl;
  std::cout << a.d3 << std::endl;
  // the following lines will not compile so you can't change the value
  // std::cout << a.d3.x << std::endl;
  // a.d2.x = 0.0;
  return(0);
}
妄司 2024-09-16 06:14:34

上面的答案似乎都没有关注一个细节:这里提到了static,因此这些常量似乎独立于类的实际实例。

换句话说:这些是全局常量。正如您所猜测的,const 关键字的存在在这里很重要,因为编译器将应用优化。

无论如何,我们的想法是使用辅助结构。

// foo.h
class Foo
{
public:
  static double const m1;
  static double const m2;
  static double const m3;
};

// foo.cpp
struct Helper
{
  double m1, m2, m3;
  Helper() { complexInit(m1, m2, m3); }
} gHelper;

double const Foo::m1 = gHelper.m1;
double const Foo::m2 = gHelper.m2;
double const Foo::m3 = gHelper.m3;

当然,在真实的程序中,我鼓励您将常量实际包装在某种接口后面,以这种方式公开它们确实是不好的做法,因为这使得更改它们(使用另一种类型)非常困难。

另请注意,您不需要输出参数的指针,普通引用则需要。

None of the answer above seemed to pay attention to a detail: static is mentioned here, so these constants seem to be independent of the actual instance of the class.

In other words: those are global constants. As you guessed, the presence of the const keyword is important here, because of the optimizations the compiler will apply.

Anyway, the idea is to use a helper structure.

// foo.h
class Foo
{
public:
  static double const m1;
  static double const m2;
  static double const m3;
};

// foo.cpp
struct Helper
{
  double m1, m2, m3;
  Helper() { complexInit(m1, m2, m3); }
} gHelper;

double const Foo::m1 = gHelper.m1;
double const Foo::m2 = gHelper.m2;
double const Foo::m3 = gHelper.m3;

Of course, in a real program, i would encourage you to actually wrap the constants behind some kind of interface, it's really bad practice to expose them this way, because it makes changing them (using another type) very difficult.

Also note that you don't need pointers for output parameters, plain references do.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文