简体   繁体   中英

Preventing copy construction and assignment of a return value reference

If I have a function that returns a reference to an instance of a class that I don't have control over its source, say list<int> :

list<int>& f();

I want to ensure that its value is only assigned to another reference, eg:

list<int> &a_list = f();

If the user were instead to do:

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

I want it to be a compile-time error since the user would be manipulating only a copy of the list and not the original list (which is never what is intended/wanted for my application).

Is there any way to prevent copy-construction and assignment in the above (say via some kind of "wrapper" class)?

Ideally, if some wrapper class were to be used, say wrapper<T> , I'd like it to work for objects of any type T .


Yes, I know that for a class that I do have control over, I can simply make the copy-constructor and assignment operator private like:

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

to forbid copy-construction and assignment; but, as shown above, I want to do this for, say, std::list for which I can not simply make the copy-constructor and assignment operator private .

This is a separate answer from my previous one, since the problem has been clarified. My previous answer might be useful to someone with sane requirements, so I leave it intact.

The desired behavior is impossible : the request is to be able to generically return something that looks exactly like a T& but that does not behave like a T . The fact that this returned thing is NOT actually a reference must be made known the the user (and compiler!) in some way.

Well, you could do it with a wrapper. Make a wrapper for your list that overloads -> but never provides you with access to the real reference. I imagine there are probably ways to work around such a method but it would have to be on purpose. Should be good enough to inform the client they really shouldn't be doing it.

You can inherit from this class, and create a matching constructor to call the constructor of the parent class (for example, have the same data constructors, that just pass the data to the parent class), and make the copy constructor and copy assignment private. Or, you could derive from both boost::noncopyable and that class. You can then use a pointer/reference to the base class safely.

EDIT : If that class has an interface, you can make a decorator that implements that interface, that is not copyable, and doesn't provide a way to get a reference/pointer to the object it's wrapping.

If neither of these are an option, you could write a class that has the same exact methods, and looks the same, without a copy constructor and copy assignment, that would call the appropriate methods of the class you're guarding (again, decorator, the hard way). But I'd avoid that.

I don't believe there's anything in the language that lets you do this. Arguably, you could return a pointer so that they have to take explicit action to copy it.

OTOH, here's an example of a wrapper you could use. Note that Uncopyable gets returned by value, not reference. (But that's okay, since it's probably just pointer-sized.)

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

I find this to be a bizarre request. The policy to copy the list or work with it as a reference should be up to the user normally, but if, for some reason, it is never correct to copy the list, then a wrapper class does the trick.

If the user knows what he's doing, he should understand the differences between working with a deep copy of the list as opposed to working with a shallow copy and modifying the original. If the user doesn't understand this, he has no business using C++ yet.

[Edit] I just noticed that someone posted an almost identical solution before. The class can be quite simple:

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

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

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

This would make it very tough for the user to copy the pointee. They'd have to call operator-> explicitly and dereference the result. Now simply return NoncopyablePtr< std::list< int > > and you'll make it awfully tough on clients (though not impossible) to make a copy of that list.

If you don't like using operator-> then I'm afraid there's really no other way to prevent users from being able to easily copy the result.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM