14

When multiple const properties of a C++ class depend on some intermediate calculation, what is the simplest way to initialize them?

For example, how do I correct the constructor for the class below?

class MyClass {
public:
    const int a;
    const int b;

    MyClass() {
        int relatedVariable = rand() % 250;
        a = relatedVariable % 100;
        b = abs(relatedVariable - 150);
    }
};
Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
Victor Lyuboslavsky
  • 9,882
  • 25
  • 87
  • 134

8 Answers8

12

With C++11, you can simply use a delegating constructor:

class MyClass
{
public:
    const int a;
    const int b;

private:
    MyClass( int relatedVariable )
      : a( relatedVariable % 100 ),
        b( abs( relatedVariable - 150 ) ) {}

public:
    MyClass() : MyClass( rand() % 250 ) {}
};
Daniel Frey
  • 55,810
  • 13
  • 122
  • 180
6

Here's a roundabout solution using delegating constructors:

class MyClass
{
    MyClass(int aa, int bb) : a(aa), b(bb) { }

    static MyClass Maker() { int x = /* ... */; return MyClass(x * 2, x * 3); }

    int const a;
    int const b;

public:
    MyClass(MyClass const &) = default;
    MyClass() : MyClass(Maker()) { }
};
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • @ulidtko: Try compiling with a current (as in bleeding edge) compiler and c++11 features turned on for that delegating constructor. – Grizzly Mar 20 '13 at 22:35
  • 1
    @ulidtko: This uses C++11 features. Your compiler might not support those. – Kerrek SB Mar 20 '13 at 22:40
  • Okay, you're right. I used gcc 4.6, but delegating constructors are supported only in 4.7+. – ulidtko Mar 20 '13 at 22:40
  • That looks too complicated, see my answer for an easier solution using delegating constructors. – Daniel Frey Mar 20 '13 at 23:14
  • @DanielFrey: that looks good. I suppose my version is a little more extensible, but for the OP's example yours is much nicer. – Kerrek SB Mar 20 '13 at 23:28
3

This will kinda work for those of us who happen to prefer being less advanced in their coding:

class MyClass {
public:

    int iamStupid;      /* K.I.S.S. */

    const int a;
    const int b;

    MyClass()
      : iamStupid(rand() % 250)
      , a(iamStupid % 150)
      , b(abs(iamStupid - 150))
    {}
};

The additional member presents an unnecessary overhead — which may or may not be significant for the task at hand. OTOH, the code is simple.

Remember to declare iamStupid before a and b! (see comments)

ulidtko
  • 14,740
  • 10
  • 56
  • 88
  • 3
    [Member variables are initialized in the order of their declaration](http://stackoverflow.com/a/1242845/201270). Therefore this would initialize `a` and `b` before initializing `relatedVariable`, which kind of defeats its purpose. – Grizzly Mar 20 '13 at 22:38
  • 1
    @Grizzly, correct. I should've declared `relatedVariable` before `a` and `b`. – ulidtko Mar 20 '13 at 22:42
2

You could do something like this - not pretty but should do the trick:

class MyClass {
public:
    const int a;
    const int b;
    static int relatedVariable;
    MyClass() :
        a(setRand()),
        b(relatedVariable)  {}
    static const int setRand()
    {
        relatedVariable = rand() % 250;
        return relatedVariable;
    }
};
int MyClass::relatedVariable = 0;
Tomer Arazy
  • 1,833
  • 10
  • 15
2

In case if you are stuck with an ancient compiler which doesn't support delegating constructors, here's the same approach adapted for the older language version:

class MyClassBase {
public:
    const int a;
    const int b;
    MyClassBase(int a, int b) : a(a), b(b) {}
};

class MyClass : public MyClassBase {
    static MyClassBase Maker() {
        int x = rand() % 250;
        return MyClassBase(x % 100, abs(x - 150));
    }
public:
    using MyClassBase::a;
    using MyClassBase::b;

    MyClass() : MyClassBase(Maker()) { }
};
ulidtko
  • 14,740
  • 10
  • 56
  • 88
1

Introduce an intermediate class that does the calculation:

class ConstCalc {

   public:
    ConstCalc(int related) : rv(related){}

    int a() const { return rv % 100; } 
    int b() const { return abs( rv - 150 ) ; } 

   private:
    const int rv;
};

class MyClass {
public:
    const int a;
    const int b;

    MyClass( const ConstCalc c ) : a( c.a() ), b( c.b() ) {
    }
};
tpdi
  • 34,554
  • 11
  • 80
  • 120
1

Const is a contract between a class's user and implementor. It indicates that the class user should not modify the member variables, thus providing an immutable object design. It is fine for a constructor to otherwise initialize that state. That said, it might be better to hide these behind a private access qualifier and to provide accessors that allow read-only. The correct way to temporarily remove const-ness is using the const_cast<>.

class MyClass {
public:
   const int a;
   const int b;

MyClass() : a(0), b(0) {
    int relatedVariable = rand() % 250;
    const_cast<int&>(a) = relatedVariable % 100;
    const_cast<int&>(b) = abs(relatedVariable - 150);
}

};

jbruni
  • 1,238
  • 8
  • 12
  • Is there any performance impact for this solution? Won't the compiler now assume that a and b aren't really const? – Victor Lyuboslavsky Mar 20 '13 at 22:30
  • 2
    It is doubtful that you will see any performance change at all. "const-ness" inside of a class is a compile-time notion. – jbruni Mar 20 '13 at 22:37
  • 3
    @jbruni, are you sure this is strictly legal? IIRC `const_cast` is only well-defined if the object itself is not `const` (i.e. you can cast a `const T&` to `T&` only if the reference refers to an object not originally declared `const`) – Stephen Lin Mar 21 '13 at 01:20
  • 2
    @jbruni see the first answer here: http://stackoverflow.com/questions/357600/is-const-cast-safe ; also, the quote "§7.1.​5.1/4 says Except that any class member declared mutable (7.1.1) can be modified, any attempt to modify a const object during its lifetime (3.8) results in undefined behavior" – Stephen Lin Mar 21 '13 at 01:21
  • yeah, this is explicitly UB, so i had to vote accordingly – underscore_d Jun 11 '16 at 18:59
-1

You could make a and b private, and provide getters to access their values from outside the class.

class MyClass
{
private:
    int a, b; // private
public:
    int getA() { return a; }
    int getB() { return b; }

    MyClass()
    {
        int relatedVariable = rand() % 250;
        a = relatedVariable % 100;
        b = abs(relatedVariable - 150);
    }
};

Or, you could just use subobject initializers and cache the random number somehow. Turning the optimization on might even remove the temporary variable in the generated program text.

class MyClass
{
private:
    int temp; // this is a workaround
public:
    const int a;
    const int b;

    MyClass() : temp(rand() % 250), a(temp % 100), b(abs(temp - 150)) {}
};

Remember that subobject construction happens in the order that members are declared in the class, and that the order of subobjects in the initialization list is ignored.

Or, you can be lazy, and only store the initial random number, and generate a, b on demand.

Community
  • 1
  • 1
Wug
  • 12,956
  • 4
  • 34
  • 54
  • 1
    *"will probably detect the overlapping computation"* - it won't, because `rand()` has side effects. – ulidtko Mar 20 '13 at 22:32
  • But `rand()` is only called once. Any method of caching the result will produce the same output, regardless of what bizarre trick is used to do it. The memory `a, b` will not be mapped into read-only memory, and what the optimizer does to it is its own prerogative. But I see what you mean and I'll reword it. – Wug Mar 20 '13 at 22:36