Delegating copy constructor and const data initialization
Asked Answered
A

2

6

I have a class A with lots of data members, some of which are constant. All data members have proper copy constructors, so I want to default a copy constructor of my class:

class A
{
public:
        A() : a(1) {}
        A(const A& op) = default;
private:
        // ... Lots of constant and non-constant member data ...
        const int a;
};

Then, I want to write a constructor which accepts a reference to A and a value which should initialize one of constant data members:

A(const A& op, const int a_);

Here op should be copied, and a should be initialized with a_ after that or instead of copying. I would like to avoid manual initialization of all data members by delegating to a copy constructor, but how to overwrite my const data member in this case? For example:

// Illegal: constructor delegation can't be mixed with field initialization.
A(const A& op, const int a_) : A(op), a(a_) {}

// Illegal: attempt to assign constant member.
A(const A& op, const int a_) : A(op) { a = a_; } 

// Hack. May lead to UB in some cases.
A(const A& op, const int a_) : A(op)
{
    *(const_cast<int*>(&a)) = a_;
    // ... or same tricks with memcpy ...
}

Obviously, all of these approaches are evil as they try to initialize a twice.

Another solution is to move all constant data to a base class and write all needed ctors, but it looks to verbose.

Is there a cleaner way to implement A(const A&, int a_)?

Awfully answered 1/8, 2017 at 8:38 Comment(6)
Is the value of a_ unconstrained?Daily
@StoryTeller what do you mean by unconstrained? a and a_ are not necessarily integer, it's just an example. In general, the constructor accepts a const reference to a value.Awfully
+1 for question as I have had this many times and just dumped everything into a nested struct with a constructor. And then, as accessing what would have been my own members is now a bit ugly I put getters and setters on the main class and pretend to both myself and others that I am soooo OO that I won't even access my on member variables without getters and setters and really had always intended to put them there. Am interested to hear what the boffs here come up with.Jurisprudence
Why int a_ isn't int const a_?Jeneejenei
@MariusBancila OK, let it be. Fixed my question.Awfully
Side note: But I doubt the standard will ever allow mixing delegation and member initialization. Const members are not so bad, but allowing a reference member to be re-bound... That's problematic.Daily
F
2

Unfortunately, C++ const field initialization is a very special case with specific syntax, so is constructor delegation, and the constructor syntax has no provision to mix them, so there can be no clean and neat solution here (at least for current C++ versions, maybe later...). The best I can imagine is:

class A
{
public:
        A() : a(1) {}
        A(const A& op):
             const_field1(op.const_field1),..., a(op.a) // all const fields here
           { init() };
        A(const A& op, int a_):
             const_field1(op.const_field1),..., a(a))   // dup line at editor level

private:
        void init(void) {
            // init non const fields here
        }
        // ... Lots of constant and non-constant member data ...
        const int a;
};

It does not make sense if you have only the copy ctor and one single additional one, but it could ease code maintenability if you have many additional ctors. It is a pity that only non const field setting can be factorized across different ctors with a private method, but C++ standard is like that.

Fortification answered 1/8, 2017 at 9:39 Comment(0)
R
0

What about a copy constructor with a full initialization list? Since your data is const, you will only be able to assign a value to it using initalization list.

A(const A& op, int a_) : 
  prop_a(op.prop_a_), 
  prop_b(op.prop_b_), 
  // continue for all members ...
  a(a_)  // except a
{
}
Rook answered 1/8, 2017 at 8:47 Comment(1)
I think that's what OP wants to avoid ("avoid manual initialization of all data members"), but that's indeed the only solution.Sable

© 2022 - 2024 — McMap. All rights reserved.