Запретить копирование при инициализации неконстантного объекта из константной ссылки - PullRequest
9 голосов
/ 29 ноября 2010

Я немного запутался в отношении семантики ссылок C ++.Предположим, у меня есть класс, который возвращает константную ссылку:

class foo
{
private:
    std::map<int, int> stuff;
public:
    const std::map<int, int>& getStuff()
    {
        return stuff;
    }
};

И я использую его следующим образом:

foo f;
const std::map<int, int>& s = f.getStuff();

, что хорошо, но если бы я использовал его следующим образом:

foo f;
std::map<int, int> s = f.getStuff();

Что именно происходит?

Если я правильно понимаю, была возвращена постоянная ссылка на stuff и создана копия в s, на которой я могу нанести ущерб.Есть ли способ избежать этого?

edit:

Так что нет способа избежать вызова конструктора копирования здесь, для std :: map в любом случае ...

Ответы [ 4 ]

5 голосов
/ 29 ноября 2010

Краткий ответ: нет, вы не можете предотвратить это.Клиент не может изменить оригинал, но если вы предоставляете клиенту доступ на чтение к карте, то клиент несет ответственность за то, что не делает глупостей с информацией;класс не может предотвратить это.

Более длинный ответ: возможно, но не совсем.Если вы действительно хотите сделать копирование трудным, вы можете обернуть карту в класс с помощью закрытого конструктора копирования и оператора присваивания.Таким образом, присвоение s будет недопустимым (отклонено компилятором).Клиент по-прежнему сможет читать элементы карты по частям и заполнять ими новую карту - ручную копию - но единственный способ предотвратить это - ограничить доступ для чтения в классе-оболочке,побеждает цель getStuff.

4 голосов
/ 29 ноября 2010
std::map<int, int> s = f.getStuff();

Это вызывает конструктор копирования std::map<int, int> и создает копию объекта.Содержимое карты stuff копируется в новую карту s.

Вы не можете нанести ущерб исходному объекту, поскольку s - новый объект, совершенно не связанный с исходным объектом, кромеиз-за того, что исходный объект и новый объект имеют одинаковое содержимое.

Нельзя законно нанести ущерб карте stuff через ссылку на констант, возвращаемую foo::getStuff().Единственный способ изменить карту - через const_cast, а изменение объекта с помощью указателя или ссылки, полученной с помощью const_cast, может привести к неопределенному поведению.

1 голос
/ 29 ноября 2010

Да, ваше понимание верно. Это ничего не значит при инициализации копирования и включает использование конструктора копирования. Невозможно избежать этой копии, поскольку это то, что запрашивает фрагмент кода, который вы показали.

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

C ++ 03 соответствующие ссылки:

$ 8,5 / 12- «Инициализация, которая происходит при передаче аргумента, функция возврат, выбрасывая исключение (15.1), обработка исключения (15.3) и заключенные в скобки списки инициализаторов (8.5.1) называется инициализацией копирования и эквивалентно виду T x = а; "

$ 8,5 / 14- "Если инициализация прямая инициализация, или если это инициализация копии, где cv-неквалифицированная версия источника тип тот же класс, что и производный класс, класс назначение, конструкторы считается. Применимый конструкторы перечислены (13.3.1.3), и выбирается лучший разрешение перегрузки (13.3). Выбранный конструктор называется для инициализации объекта, с выражение (я) инициализатора как его аргумент (ы). Если нет конструктора применяется, или разрешение перегрузки неоднозначно, инициализация плохо сформирован. "

0 голосов
/ 29 ноября 2010

Да, это создаст копию карты.

Что касается вашего вопроса - зависит от того, сколько вы хотите избежать.В общем, если это класс, который вы объявили сами, вы можете сделать конструктор копирования или operator = закрытым, чтобы предотвратить его использование, но, очевидно, это запретит вам несколько вещей.

...