Сложная инициализация полей константы

Рассмотрите класс как этот:

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), который вычисляет три константы во времени выполнения. Предположим, что вычисление довольно сложно, и по сути производит все три значения сразу. Кроме того, результаты зависят от конфигурации сборки, таким образом, жесткое кодирование результаты не является опцией.

Существует ли разумный способ сохранить эти вычисленные значения в соответствующей константе двойные поля класса?

В противном случае можно ли предложить более естественный способ объявить такой класс в C++?

В C# я использовал бы статический класс со статическим конструктором здесь, но это не опция в C++. Я также считал создание ImportantConstant1.. 3 или поля неконстанты или вызовы функции, но оба кажутся нижними.

Единственный способ инициализировать поля константы, которые я нашел, состоит в том, чтобы использовать списки инициализатора, но не кажется возможным передать результаты мультивыходного вычисления в таком списке.

11
задан Community 23 May 2017 в 12:33
поделиться

7 ответов

Почему вы не можете сделать:

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

Вот так и превратить функцию вычислений в фабричную?

9
ответ дан 3 December 2019 в 04:30
поделиться

во-первых - вы можете творить зло: отбросьте конст в ComputeImportantConstants() и поместите туда значения. Не делайте этого, потому что тогда вы лжете компилятору, и он попытается найти самый неприятный способ окупиться.

second:

сделайте что-то вроде этого:

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

вы все равно можете улучшить этот класс, сделав его своего рода синглтоном или около того (так как вы хотите, чтобы вычисление выполнялось только один раз).

5
ответ дан 3 December 2019 в 04:30
поделиться

Вы можете переместить 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);
};
3
ответ дан 3 December 2019 в 04:30
поделиться

Единственный способ инициализировать константные поля, который я нашел, - это использовать списки инициализаторов, но не представляется возможным передать результаты вычислений с несколькими выходами в таком списке.

Это правда; однако вы можете инициализировать один член - структуру констант. Увидеть ниже.

Я также подумал о том, чтобы сделать ImportantConstant1..3 либо неконстантными полями, либо вызовами функций, но оба кажутся неполноценными.

Я не думаю, что функции получения будут неполноценными. Компилятор, скорее всего, встроит их. Примите во внимание следующее:

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 , а также все его поля являются постоянными, значения не могут быть изменены другими методами-членами - только в коде, который вы набросали в своем вопросе. Инициализация может быть используется здесь, поскольку мы инициализируем одно значение: структуру.

Доступ к константам (скорее всего) будет таким же эффективным, как и раньше: предложение встроить функции, и компилятор, скорее всего, сделает это, учитывая, насколько малы геттеры.

2
ответ дан 3 December 2019 в 04:30
поделиться

Просто разделите объект на простую для инициализации и сложную часть, а затем инициализируйте сложную часть через конструктор копирования:

// 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()) {}
};
1
ответ дан 3 December 2019 в 04:30
поделиться

Ни один из приведенных выше ответов не обращал внимания на детали: здесь упоминается 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;

Конечно, в реальной программе я бы посоветовал вам на самом деле заключить константы в какой-то интерфейс, это действительно плохая практика - раскрывать их таким образом, потому что это очень затрудняет их изменение (с использованием другого типа).

Также обратите внимание, что вам не нужны указатели для выходных параметров, в отличие от простых ссылок.

1
ответ дан 3 December 2019 в 04:30
поделиться

Как насчет чего-то вроде этого:

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);
}
1
ответ дан 3 December 2019 в 04:30
поделиться
Другие вопросы по тегам:

Похожие вопросы: