Предотвращение создания копии и присвоения ссылки на возвращаемое значение - PullRequest
4 голосов
/ 24 июня 2010

Если у меня есть функция, которая возвращает ссылку на экземпляр класса, который я не могу контролировать над его источником, скажем, list<int>:

list<int>& f();

Я хочу убедиться, что его значение только присвоено другой ссылке, например ::100100

list<int> &a_list = f();

Если бы пользователь вместо этого делал:

list<int> a_list = f(); // note: no '&', so the list is copied

Я хочу, чтобы это была ошибка во время компиляции, поскольку пользователь будет манипулировать только копией списка, а не оригинальным списком (что никогда не является тем, что предназначено / требуется для моего приложения).

Есть ли какой-либо способ предотвратить конструирование и назначение копирования в вышеприведенном (скажем, с помощью некоторого класса «обертки»)?

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


Да, я знаю, что для класса, над которым я делаю , я могу просто сделать конструктор копирования и оператор присваивания private, например:

class MyClass {
public:
    // ...
private:
    MyClass( MyClass const& );
    MyClass operator=( MyClass const& );
};

запретить копирование-конструкцию и переуступку; но, как показано выше, я хочу сделать это, скажем, для std::list, для которого я не могу просто сделать конструктор копирования и оператор присваивания private.

Ответы [ 5 ]

2 голосов
/ 24 июня 2010

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

Требуемое поведение невозможно : запрос должен иметь возможность вернуть что-то, что в точности соответствует T&, но не ведет себя как T. Тот факт, что эта возвращенная вещь на самом деле НЕ является ссылкой, должен быть каким-то образом доведен до сведения пользователя (и компилятора!).

1 голос
/ 24 июня 2010

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

Если пользователь знает, что он делает, он должен понимать разницу между работой с глубокой копией списка по сравнению с работой с мелкой копией и изменением оригинала.Если пользователь не понимает этого, у него пока нет никакого дела с использованием C ++.

[Редактировать] Я только что заметил, что кто-то ранее публиковал почти идентичное решение.Класс может быть довольно простым:

template <class T>
class NoncopyablePtr
{
private:
    T* ptr;

public:
    /*implicit*/ NoncopyablePtr(T* iptr): ptr(iptr) {}

    T* operator->() const
    {
        return ptr;
    }
};

Это сделает очень трудным для пользователя копирование pointee.Им придется вызывать оператор-> явно и разыменовывать результат.Теперь просто верните NoncopyablePtr >, и вам будет очень тяжело создавать копии этого списка на клиентах (хотя и не невозможно).

Если вам не нравится использование оператора-> тогда я боюсь, что на самом деле нет другого способа помешать пользователям легко скопировать результат.

1 голос
/ 24 июня 2010

Вы можете наследовать от этого класса и создать соответствующий конструктор для вызова конструктора родительского класса (например, иметь те же конструкторы данных, которые просто передают данные в родительский класс), и создать конструктор копирования икопия задания приватная.Или вы можете получить как из boost :: noncopyable, так и из этого класса.Затем вы можете безопасно использовать указатель / ссылку на базовый класс.

РЕДАКТИРОВАТЬ: Если этот класс имеет интерфейс, вы можете сделать декоратор, который реализует этот интерфейс, который не копируется и не обеспечиваетспособ получить ссылку / указатель на объект, который он переносит.

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

1 голос
/ 24 июня 2010

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

OTOH, вот пример оболочки, которую вы можете использовать. Обратите внимание, что Uncopyable возвращается по значению, а не по ссылке. (Но это нормально, так как это, вероятно, только размер указателя.)

#include <iostream>
#include <list>

template <typename T>
class Uncopyable
{
public:
    Uncopyable(T& r) : ref(r) {}

    T* operator->() { return &ref; }

private:
    T& ref;
};

Uncopyable<std::list<int> > get_list()
{
    static std::list<int> l;
    l.push_back(l.size());
    return l;
}

int main() 
{
    for (int i = 0; i < 10; ++i)
    {
        Uncopyable<std::list<int> > my_l = get_list();
        std::cout << my_l->size() << std::endl;
    }
}
1 голос
/ 24 июня 2010

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

...