How to initialize multiple constant member variabl

2019-08-12 18:45发布

问题:

Introduction

Let's introduce this simple example:

#include <cmath>

class X
{
public: // Members

    /// A ^ B + A
    int A;

    /// A ^ B + B
    int B;

public: // Specials

    X(
        const int & A,
        const int & B
    )
        : A(A)
        , B(B)
    {
        const auto Pow = static_cast<int>(std::pow(A, B));

        this->A += Pow;
        this->B += Pow;
    }
};

Trivia

  • Introduced class has two member variables: A and B.
  • They take value of A ^ B + A and A ^ B + B, respectively.
  • Both of them shares common complex initialization code (let's assume std::pow is complex).

Problem

I'd like to make both A and B members const.

Question

How to do that without repeating complex initialization (ie avoid calling std::pow twice)?

What I've tried

#include <cmath>

class X
{
public: // Members

    /// A ^ B + A
    const int A;

    /// A ^ B + B
    const int B;

public: // Helpers

    struct Init
    {
    public: // Members

        int A;
        int B;

    public: // Specials

        Init(
            const int & A,
            const int & B
        )
            : A(A)
            , B(B)
        {
            const auto Pow = static_cast<int>(std::pow(A, B));

            this->A += Pow;
            this->B += Pow;
        }
    };

public: // Specials

    X(
        const Init& Init
    )
        : A(Init.A)
        , B(Init.B)
    {};

    X(
        const int & A,
        const int & B
    )
        : X(Init(
            A,
            B
        ))
    {};
};
  1. Create struct Init that takes role of past version of class X.
  2. Make X members const while keep Init members non const.
  3. Use constructor delegation to redirect constructor arguments to Init.
  4. Move non const member variables from Init to X and make them const.
    • Note there is no std::move as int is TriviallyCopyable.

However, my solution seems overcomplicated. Any help would be appreciated.

No goals

  • Make another X member variable that will store common code result (ie std::pow).
  • Add another level of indirection outside X class (eg introduce base class for X).

Note

Solutions can use newer versions of C++ than C++11.

回答1:

Using a delegating constructor is a good option for such cases.

class X
{
   public: // Members

      /// A ^ B + A
      const int A;

      /// A ^ B + B
      const int B;

   public:

      X(int a, int b) : X(a, b, func1(a, b)) {}

   private:

      X(int a, int b, int c) : A(func2(a, b, c)), B(func3(a, b, c)) {}

      static int func1(int a, int b) { return std::pow(a,b); }
      static int func2(int a, int b, int c) { return (a + c); }
      static int func3(int a, int b, int c) { return (b + c); }
};

The logic/computation in func1, func2, and func3 can be as simple or as complex as you need.



回答2:

You can solve this by using a factory function. You make the constructor of X private and then use a friend/static function to get objects of X. Then you can do the complex code in the body of the function and then pass those values to the constructor of X. That would look something like

class X
{
public:
    const int A;
    const int B;
    friend X make_X(int a, int b)
    {
        // do complex stuff
        return X(complex_result1, complex_result2);
    }
    // or
    static X make(int a, int b)
    {
        // do complex stuff
        return X(complex_result1, complex_result2);
    }
private:
    X(const int  A, const int  B) : A(A), B(B) {}
};

and would be used like

X foo = make_x(a, b);
//or
X foo = X::make(a, b);