Можно ли написать разные copyCtor для константных и неконстантных экземпляров?

У меня есть следующая проблема:

У меня есть класс, который должен сделать это:

Obj o;
Obj o1(o), o1=o; // deep-copies
const Obj c(o), c=o; // deep-copies
const Obj c1(c), c1=c; // shallow-copies
Obj o2(c), o2=c; // deep-copies

Как я могу сделать это желательно без наследства? (Я имею в виду, я бы сделал Const_obj наследование от Obj иначе.)

РЕДАКТИРОВАТЬ:

С помощью o.clone() напрямую не вариант, потому что тогда я мог легко ввести ошибки случайно не Клонирование.

РЕДАКТИРОВАТЬ:

Наконец, есть правильное, полное решение с ленивой оценкой с использованием идеи Эффективный C ++ Скотт Мейерс. Проверьте мой ответ ниже.

7

Решение

Нет, ты не можешь.

  • Конструктор не может быть cv-квалифицированным, поэтому вы не можете заставить его создать объект const.
  • Тип возврата функции (включая операторы) не является частью ее сигнатуры, поэтому вы не можете перегрузить функцию, просто изменив ее тип возврата.

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

4

Другие решения

После прочтения Эффективный C ++ Скотт Мейерс, следующее решение:

определить шаблон, который выполняет ленивую оценку (с подсчетом ссылок):

class Obj : private lazy<Obj_data>{};

и ленивый хранит Obj_data в частном порядке, имеет защищенные средства доступа, один для модификации, один для доступа только для чтения.
Модификатор-аксессор сначала глубоко копирует Obj_data если необходимо, то сдает ссылку на данные. Метод доступа только для чтения просто возвращает константную ссылку.

Общая стоимость — хранение 2 дополнительных указателей (один для данных и один для счетчика) и счетчик.

Реализация примерно такая:

class lazy{
protected:
lazy(const lazy&obj){lazy_copy(obj);}
//(the required constructors, operator= ...)

// accessors:
const Obj_data& data() const {return *od;}
Obj_data& mod_data() {make_private(); return *od;}
private:
void lazy_copy(const lazy& obj);
void make_private(); // this does the actual deep-copy, as late as possible.
private:
counter*;
Obj_data* od;
};

Таким образом, чтение и изменение атрибута Obj идет

void Obj::method(){
cout << data().some_attribute;    // simple read
mod_data().i = 10;                // simple modify
const Obj_data& const_d = data(); // assignable for lots of read-outs
Obj_data& var_d = mod_data();     // assignable for lots of modifications.
}

Обратите внимание, что вы можете использовать только data() в const член как mod_data() это неконстантная функция в классе, поэтому это решение абсолютно безопасно с небольшими накладными расходами.

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

2

Вы можете, частично, с фиктивным аргументом:

class C {
public:
struct NonStandardCopy { };

C (const C &) {
// "ordinary" copy constructor with default behavior
}

C (const C &, NonStandardCopy) {
// "other" "copy" constructor
}
};

C c = c1; // default
C c (c1); // default
C c (c1, C::NonStandardCopy ()); // non-default

РЕДАКТИРОВАТЬ: подход только клонов может быть тем, что вы хотите (вместе с семантикой перемещения, производительность может быть не слишком большой):

class C {
private:
struct DeepCopy { };
struct ShallowCopy { };

C (const C &) = delete;

C (const C &, DeepCopy) {
// deep copy
}

C (const C &, ShallowCopy) {
// shallow copy
}
public:
// move constructor
C (C && other) = default;

const C clone () const { // 1
// shallow copy
return C (*this, ShallowCopy ());
}

C cloneToNonConst () const {  // 2
// deep copy
return C (*this, DeepCopy ());
}

C clone () { // 3
return cloneToNonConst ();
}
};

C o;
C o1 = o.clone (); // call 3
const C o2 = o1.clone (); // call 3
const C o3 = o2.clone (); // call 1
C c4 = o3.cloneToNonConst (); // call 2; o3.clone () will give error
0
По вопросам рекламы [email protected]