Код, с которым я работаю, имеет собственную реализацию интеллектуального указателя, которая выполняет простой подсчет ссылок. Да, у нас не должно быть собственной реализации. Да, мы должны использовать один из Boost или что-то подобное. Потерпи меня.
Я обнаружил, что хотел написать код, подобный этому:
...
CountedPointer<Base> base;
...
CountedPointer<Derived> derived;
...
base = derived;
Однако конструктор копирования для CountedPointer имеет такой прототип, как этот:
CountedPointer(const CountedPointer<T> &other);
Таким образом, приведенный выше код не будет компилироваться, поскольку он не может найти подходящий конструктор (или оператор присваивания - там такая же история). Я попытался переписать конструктор копирования с помощью прототипа так:
template<U>
CountedPointer(const CountedPointer<U> &other);
Однако я столкнулся с проблемой, заключающейся в том, что конструктор копирования должен получить доступ к закрытому члену объекта, который он копирует (т. Е. Необработанный указатель), и если он имеет другую специализацию CountedPointer, они не видны.
Александреску избегает этой проблемы в своей библиотеке Локи , имея функции доступа для инкапсулированного указателя, но я бы предпочел не предоставлять прямой доступ к необработанному указателю, если это возможно.
Можно ли как-нибудь написать это, чтобы разрешить производную базовую копию, но не разрешить общий доступ к необработанному указателю?
Обновление:
Я реализовал принятый ответ ниже, и он работает хорошо. Я потратил некоторое время на то, чтобы выяснить, почему моя программа ужасно зависла, когда я предоставил только шаблонную версию конструктора копирования, заменив исходную не шаблонную версию. В конце концов я понял, что компилятор не рассматривает шаблонную версию как конструктор копирования и предоставляет версию по умолчанию. По умолчанию просто копирует содержимое без обновления счетчика, поэтому я получаю висячие указатели и двойные освобождения. То же самое относится и к оператору присваивания.