Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prevent copying when initializing an non-const object from a const reference

I am a little confused right now regarding C++ reference semantics. Suppose I have a class that returns a const reference:

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

And I use it as follows:

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

which is fine, but if I were to use it as follows:

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

What happens exactly?

If I understand correctly, a const reference to stuff was returned and a copy created into s on which I can wreak havoc. Would there be any way to avoid this?

edit:

So there is no way to avoid the copy constructor being called here, for std::map anyways...

like image 354
Victor Parmar Avatar asked Nov 29 '10 04:11

Victor Parmar


People also ask

Can a const reference refer to a non-const object?

No. A reference is simply an alias for an existing object. const is enforced by the compiler; it simply checks that you don't attempt to modify the object through the reference r .

Is it necessary to have const for a reference in copy constructor?

According to C++ copy constructor, we pass an object by reference to the copy function Object() { [native code] }, and we usually pass it as a const reference. One justification for passing a const reference is that it can use const wherever possible in C++ to avoid unintentionally changing objects.

Is const reference a copy?

Not just a copy; it is also a const copy. So you cannot modify it, invoke any non-const members from it, or pass it as a non-const parameter to any function. If you want a modifiable copy, lose the const decl on protos .

Should I always use const reference?

Yes, you should use const whenever possible. It makes a contract that your code will not change something. Remember, a non-const variable can be passed in to a function that accepts a const parameter.


2 Answers

Short answer: no, you can't prevent it. The client can't modify the original, but if you give the client read-access to the map, then the client is responsible for not doing stupid things with the information; the class can't possibly prevent that.

Longer answer: maybe, but not really. If you really want to make copying difficult, you can wrap the map in a class with private copy constructor and assignment operator. That way the s assignment will be illegal (rejected by the compiler). The client will still be able to read the elements of the map piecemeal and populate a new map with them -- a manual copy -- but the only way to prevent that is to restrict the read-access in the wrapper class, which kind of defeats the purpose of getStuff.

like image 147
Beta Avatar answered Oct 21 '22 21:10

Beta


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

This invokes the std::map<int, int> copy constructor and makes a copy of the object. The contents of the stuff map are copied into the new map s.

You can't wreak havoc with the original object because s is a new object completely unrelated to the original object, aside from the fact that the original object and the new object have the same contents.

It is impossible to legitimately wreak havoc with the stuff map via the const reference returned by foo::getStuff(). The only way you could modify the map would be through a const_cast, and modifying an object via a pointer or reference obtained through a const_cast may yield undefined behavior.

like image 28
James McNellis Avatar answered Oct 21 '22 21:10

James McNellis