views:

125

answers:

5

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, e.g.:

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.

+1  A: 

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.

Noah Roberts
If the user must user operator->, then s/he can't simply write the correct form as I showed above.
Paul J. Lucas
GMan
"correct form"? WTF is that supposed to mean? If you insist on an answer that is impossible then I'm afraid you're stuck begging from the Gods to change the universe. I tried to provide you with something that would address the problem you're trying to address. If that's not good enough...well, good luck then.
Noah Roberts
"Correct form" means one could write it as "normal": list<int> .
Paul J. Lucas
+1  A: 

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.

fingerprint211b
Per my added text above, it's not possible to inherit from some arbitrary class T and have matching constructors.
Paul J. Lucas
The absolute best you're going to be able to do with this method is catch everything with a default constructor. Given your other requirements, though, this may be your best option.
pkh
@pkh: I don't understand what you mean by "catch everything with a default constructor." Perhaps you could write some code (?).
Paul J. Lucas
I mean that the solution described in this answer works for any class that has a 0-argument constructor. "default" was an incorrect word choice.
pkh
+1  A: 

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;
    }
}
pkh
The problem with that is that the user has to know about Uncopyable and use it in their code. Ideally, I'd like the solution to be transparent.
Paul J. Lucas
Then you're boned.
pkh
@pkh: That in and of itself if an answer. I just wanted to make sure that there really was no clever way to do what I want.
Paul J. Lucas
See my comments at the top of my post; if you want this behavior badly enough, go to a pointer. What you're asking for is to treat a reference as not being the thing it refers to, which is exactly its point of existing.
pkh
+2  A: 

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.

pkh
A: 

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.