Calling another constructor when building an object with const elements

I have a class with const members and one constructor that calls another constructor with added values. Usually I could use a colon initializer for this, but the function is complex ( printf / sprintf - for example) and requires me to use the variable on the stack, so I have to do this in the constructor body and use assign *this to the new object. But of course this is not true because my member variables are const .

 class A { public: A(int b) : b(b), c(0), d(0) // required because const { int newC = 0; int newD = 0; myfunc(b, &newC, &newD); *this = A(b, newC, newD); // invalid because members are const // "cannot define the implicit default assignment operator for 'A', because non-static const member 'b' can't use default assignment operator" // or, sometimes, // "error: overload resolution selected implicitly-deleted copy assignment operator" }; A(int b, int c, int d) : b(b), c(c), d(d) { }; const int b; const int c; const int d; }; A a(0); 

(I did not explicitly delete the assignment operator.) I declared const members because I would like them to be publicly available, but not mutable.

Is there any canonical way to solve this problem without using scary throws and forcibly redefining const ness members? What is the best solution here?

+4
source share
4 answers

You can add a parameter class and use either the C ++ 11 constructor delegation or the base class:

 struct parameters { int b; int c; int d; parameters(int b): b(b), c(), d() { myfunc(b, &c, &d); } }; // constructor delegation class A { public: A(int b): A(parameters(b)) { } A(parameters p): b(pb), c(pc), d(pd) { } }; // base/wrapper class ABase { ABase(parameters p): b(pb), c(pc), d(pd) { } }; class A: public ABase { public: A(int b): ABase(parameters(b)) { } }; 
+2
source

How to create a helper function:

 class A { static int initializor(int b) { int n; myfunc(b, &n); return n; } public: explicit A(int b_) : b(b_), c(initializor(b_)) { } A(int b_, int c_) : b(b_), c(c_) { } // ... as before ... }; 
+2
source

I prefer Kerrek SB's answer , but in your case there is a complication that you cannot easily make separate initialization functions for each member.

In this case, another solution is to move the members to the base class and initialize this base class with a helper class with non-constant members. Your initialization code moves into the constructors of the helper class and can be assigned without problems.

 class A_init { public: A_init(int b) { // do whatever you like with c and d: c = ...; d = ...; } int c; // Note: non-const int d; // Note: non-const }; class A_base { public: A_base(int b, A_init init) : b(b), c(init.c), d(init.d) {} A_base(int b, int c, int d) : b(b), c(c), d(d) {} const int b; const int c; const int d; }; class A : public A_base { public: A(int b) : A_base(b, A_init(b)) {} A(int b, int c, int d) : A_base(b, c, d) {} }; 

If you want to restrict access to A_init , you can go to private and declare A friend.

+1
source

Where can I put the results of myfunc so that it can be installed and used from different mem initializers? What about the default argument?

 class A { private: struct InitData; public: A(int b, InitData data=InitData()); A(int b, int c, int d) : b(b), c(c), d(d) { }; const int b; const int c; const int d; }; struct A::InitData { int setup(int b); int c; int d; }; inline int A::InitData::setup(int b) { myfunc(b, &c, &d); return b; } inline A::A(int b_, InitData data) : b(data.setup(b_)), c(data.c), d(data.d) {} A a(0); 

Since the compiled type is private and has no transformations, there is a small risk of accidentally using it or abusing it.

0
source

Source: https://habr.com/ru/post/1395227/


All Articles