12

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.

Community
  • 1
  • 1
Roman Starkov
  • 59,298
  • 38
  • 251
  • 324
  • If it's possible, can you tell how `ComputeImportantConstants` is implemented? Is it rather long? How do the three constants interact, what other factors are involved? – Frerich Raabe Jul 15 '10 at 14:09

8 Answers8

10

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?

wheaties
  • 35,646
  • 15
  • 94
  • 131
  • one improvement idea: make one static variable in ComputeImportantConstants() and return this variable once everything is computed. This way, subsequent calls of ComputeImportantConstants do not trigger an additional computation. – Tobias Langner Jul 15 '10 at 14:07
  • 1
    C++ nitpick: returning a `const double` from a function doesn't make much sense. It only makes life harder than necessary for the caller without improving security. The member variables are returned by value, after all. – Frerich Raabe Jul 15 '10 at 14:11
  • There is nothing wrong with a struct having no methods and only holding data (POD types.) On the other hand, it's often far easier to affect change within a program if what is being accessed is hidden behind a function call. Most people are going to prefer encapsulation because of the latter. – wheaties Jul 15 '10 at 15:07
  • The nitpick is wrong - there's no `const double` returned, only a `double` is returned by a `const` method, i.e. a method that guarantees not to change the content. – Chris Sep 05 '12 at 17:43
  • 2
    @Chris: If you look at the change history you will see that about two years ago there *was* a const double. ;-) – Frerich Raabe Sep 05 '12 at 19:42
5

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).

Tobias Langner
  • 10,634
  • 6
  • 46
  • 76
3

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);
};
Anthony Williams
  • 66,628
  • 14
  • 133
  • 155
  • Works nicely; just needed to change to public MyBase inheritance and make the const double fields public. Also, in the real code the three values are already passed around in a struct so I don't need an extra Initializer struct. (I should really have written my question using that struct...) – Roman Starkov Jul 15 '10 at 15:22
2

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.

Frerich Raabe
  • 90,689
  • 19
  • 115
  • 207
2

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.

jotik
  • 17,044
  • 13
  • 58
  • 123
1

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()) {}
};
Nordic Mainframe
  • 28,058
  • 10
  • 66
  • 83
1

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);
}
Tomek
  • 4,554
  • 1
  • 19
  • 19
1

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.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722