Делегированный конструктор копирования и инициализация константных данных

У меня есть класс A с большим количеством членов данных, некоторые из которых являются постоянными. Все члены данных имеют надлежащие конструкторы копирования, поэтому я хочу по умолчанию использовать конструктор копирования моего класса:

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

Затем я хочу написать конструктор, который принимает ссылку на A и значение, которое должно инициализировать один из постоянных членов данных:

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

Вот op должны быть скопированы, и a должен быть инициализирован с a_ после этого или вместо копирования. Я хотел бы избежать ручной инициализации всех элементов данных путем делегирования конструктору копирования, но как перезаписать мой элемент данных const в этом случае? Например:

// 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 ...
}

Очевидно, что все эти подходы являются злыми, поскольку они пытаются инициализировать a дважды.

Другое решение состоит в том, чтобы переместить все постоянные данные в базовый класс и записать все необходимые ctors, но это выглядит многословно.

Есть ли более чистый способ реализации A(const A&, int a_)?

2 ответа

Решение

К сожалению, инициализация константного поля C++ - это особый случай со специфическим синтаксисом, поэтому делегирование конструктора и синтаксис конструктора не предусматривают их смешивание, поэтому здесь не может быть чистого и аккуратного решения (по крайней мере, для текущих версий C++, может быть, потом...). Лучшее, что я могу себе представить, это:

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;
};

Это не имеет смысла, если у вас есть только копирующий ctor и один единственный дополнительный, но это может упростить поддержку кода, если у вас есть много дополнительных ctors. Жаль, что только неконстантные поля могут быть разложены по частям с помощью частного метода, но стандарт C++ такой.

А как насчет конструктора копирования с полным списком инициализации? Поскольку ваши данные являются постоянными, вы сможете назначить им значение только с помощью списка инициализации.

A(const A& op, int a_) : 
  prop_a(op.prop_a_), 
  prop_b(op.prop_b_), 
  // continue for all members ...
  a(a_)  // except a
{
}
Другие вопросы по тегам