Initialize multiple constant class members using one function call C++

C++OopConstructorConstantsInitializer List

C++ Problem Overview


If I have two different constant members variables, which both need to be initialized based on the same function call, is there a way to do this without calling the function twice?

For example, a fraction class where numerator and denominator are constant.

int gcd(int a, int b); // Greatest Common Divisor
class Fraction {
public:
    // Lets say we want to initialize to a reduced fraction
    Fraction(int a, int b) : numerator(a/gcd(a,b)), denominator(b/gcd(a,b))
    {

    }
private:
    const int numerator, denominator;
};

This results in wasted time, as the GCD function is called twice. You could also define a new class member, gcd_a_b, and first assign the output of gcd to that in the initializer list, but then this would lead to wasted memory.

In general, is there a way to do this without wasted function calls or memory? Can you perhaps create temporary variables in an initializer list?

C++ Solutions


Solution 1 - C++

> In general, is there a way to do this without wasted function calls or memory?

Yes. This can be done with a delegating constructor, introduced in C++11.

A delegating constructor is a very efficient way to acquire temporary values needed for construction before any member variables are initialized.

int gcd(int a, int b); // Greatest Common Divisor

class Fraction {
public:
    // Call gcd ONCE, and forward the result to another constructor.
    Fraction(int a, int b) : Fraction(a,b,gcd(a,b))
    {
    }
private:
    // This constructor is private, as it is an
    // implementation detail and not part of the public interface.
    Fraction(int a, int b, int g_c_d) : numerator(a/g_c_d), denominator(b/g_c_d)
    {
    }
    const int numerator, denominator;
};

Solution 2 - C++

The member vars are initialized by the order they are declared in the class declaration, hence you can do the following (mathematically)

#include <iostream>
int gcd(int a, int b){return 2;}; // Greatest Common Divisor of (4, 6) just to test
class Fraction {
public:
    // Lets say we want to initialize to a reduced fraction
    Fraction(int a, int b) : numerator{a/gcd(a,b)}, denominator(b/(a/numerator))
    {    
    }
//private:
    const int numerator, denominator;//make sure that they are in this order
};
//Test
int main(){
    Fraction f{4,6};
    std::cout << f.numerator << " / " << f.denominator;
}

No need for calling another constructors or even making them.

Solution 3 - C++

@Drew Dormann gave a solution similar to what I had in mind. Since OP never mentions not being able to modify the ctor, this can be called with Fraction f {a, b, gcd(a, b)}:

Fraction(int a, int b, int tmp): numerator {a/tmp}, denominator {b/tmp}
{
}

Only this way there is no second call to a function, constructor or otherwise, so it's no wasted time. And it's no wasted memory since a temporary would have to be created anyway, so you may as well make good use of it. It also avoids an extra division.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionQq0View Question on Stackoverflow
Solution 1 - C++Drew DormannView Answer on Stackoverflow
Solution 2 - C++asmmoView Answer on Stackoverflow
Solution 3 - C++a concerned citizenView Answer on Stackoverflow