19

I've a class which stores a reference to its parent, the reference is passed in the constructor. If I try to copy an instance I get an error "error C2582: 'operator =' function is unavailable" presumably down to the reference being non-assignable.

Is there a way around this, or do I just change the variable to pointer instead of reference?

e.g (over-simplified but I think has the key points):

class MyClass
{
public:
 MyClass(OtherClass &parent) : parent(parent) {}
private:
 OtherClass &parent;
};

MyClass obj(*this);
.
.
.
obj = MyClass(*this);
Mr. Boy
  • 60,845
  • 93
  • 320
  • 589
  • Maybe compiler is confused with the same parameter and member names? – Alex F Oct 01 '10 at 16:50
  • A reference is always constant. Once you assign it with a value (in your constructor), it is impossible to change its value. So, if you want to support affectations, you change it into a pointer. – ThunderPhoenix Apr 13 '17 at 08:36

7 Answers7

26

There is a way to do it and still use a reference, use a reference_wrapper. So

    T& member;

becomes

    std::reference_wrapper<T> member;

Reference wrappers are basically just re-assignable references.

Tiedye
  • 544
  • 1
  • 4
  • 7
  • This seems like the best way if C++11 and beyond is available. – wawiesel Aug 05 '17 at 09:42
  • This should be the accepted answer now. Solved all my problems in the best readable way possible. – doomista Nov 12 '18 at 09:52
  • Thank you so much! This answer alone taught me a lot of C++! Everything makes sense now. – Reza Hajianpour Dec 26 '18 at 03:46
  • This is much preferable to a pointer since you clearly communicate that the `member`-value can't be null. If it's a pointer, someone else always have to ask themselves if it's _valid_ that the pointer is null or not. – Joakim Thorén Oct 27 '20 at 13:22
  • Not the best way there is a big inconvenient to this solution: incomplete type not allowed. In most cases when you hold a reference you need it to be forward declared. I'd use pointer – thp9 Mar 26 '23 at 22:13
15

I don't recommend this at all

but if you are really gung ho about doing this:

#include <new>

MyClass::MyClass(const MyClass &rhs): parent(rhs.parent)
{
}

MyClass &MyClass::operator=(const MyClass &rhs)
{
    if (this!=&rhs)
    {
        this->~MyClass();
        new (this) MyClass(rhs);
    }

    return *this;
}
MSN
  • 53,214
  • 7
  • 75
  • 105
  • 2
    Warning: This approach is undefined behavior. "If, ... a new object is created at the storage location which the original object occupied, ... will automatically refer to the new object and, once the lifetime of the new object has started, can be used to manipulate the new object, if: ... **the type of the original object** is not const-qualified, and, if a class type, **does not contain any non-static data member whose type is const-qualified or a reference type** ..." – Ben Voigt Mar 16 '18 at 15:04
12

Yes, if you need to support assignment, making it a pointer instead of a reference is nearly your only choice.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
6

Yes just make the member a pointer. A reference won't be able to be reseated, and there is no work-around.

Edit: @"Steve Jessop" makes a valid point to how work-around the problem using the PIMPL idiom (private implementation using a "d-pointer"). In an assignment, you will delete the old implementation and create a new one copy-constructed from the source object's d-pointer.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • 3
    "there is no work-around". Just for that, I'm going to mention the possibility of mis-implementing `operator=` to explicitly call the destructor, then construct with placement new and the copy constructor. Or more validly, use pimpl and have `operator=` do a copy-and-swap. The impl itself doesn't need `operator=`, and can still use a reference. Still, it's a lot of boilerplate to write if this is the only feature of pimpl desired... – Steve Jessop Oct 01 '10 at 20:43
  • @Steve Jessop: Sorry, but can you please explain a bit more. I am not sure if I understand the workaround – Chubsdad Oct 02 '10 at 04:18
  • @Chubsdad the idea is that your class will be like `class MyClass { class Pimpl; Pimpl *p; public: /* dtor and ctors ... */ void swap(MyClass &m); MyClass(MyClass const&m); MyClass &operator=(MyClass m); };` and in the C++ file `class MyClass::Pimpl { Parent &parent; }; MyClass::MyClass(MyClass const& m):p(new Pimpl(*m.p)) { } MyClass &operator=(MyClass m) { swap(m); return *this; } void MyClass::swap(MyClass &m) { std::swap(p, m.p); }`. This in some kind could be regarded as a "work around", I suspect :) – Johannes Schaub - litb Oct 02 '10 at 08:52
  • Yeah, what he said but with more linebreaks ;-). As I said it's a lot of boilerplate just to avoid a pointer, and on reflection litb's right: if it's a work-around then it's a very long way round. – Steve Jessop Oct 02 '10 at 11:32
  • But does not 'new Pimpl(*m.p)' reintroduce the original copying back to us? I think this approach just shifts the problem from MyClass to Pimpl without actually solving it in either place. Not sure if I still get it. – Chubsdad Oct 03 '10 at 01:27
  • @Chubsdad nono. The copy constructor is used... You can copy references. – Johannes Schaub - litb Oct 03 '10 at 10:22
  • I doubt it litb. This is because a reference is not treated as a first class object (3.9/8). Also it is unspecified if a reference has storage or not. So can references really be copied? I could be wrong though. – Chubsdad Oct 03 '10 at 12:11
  • @Chubsdad: There's no problem with creating a new impl and adjusting the pimpl pointer. (Destruction/recreation in place is NOT permitted, see [basic.life]). Creation of the new object will initialize a new reference, binding it to the target of the source reference. Whether you want to call that copying or not doesn't matter. – Ben Voigt Mar 16 '18 at 15:07
2

You need to implement a copy constructor and initialize the reference in that copy constructor, to point to the same reference as the original object.

florin
  • 13,986
  • 6
  • 46
  • 47
1

I would make it a boost::shared_ptr. You can be pretty rough with these and they take care of themselves. Whereas using a raw pointer means tha you have to worry about that object being kept alive

pm100
  • 48,078
  • 23
  • 82
  • 145
  • That's no difference from using a reference... you have to trust the object referred to still exists whether using a reference or raw pointer. – Mr. Boy Oct 02 '10 at 09:38
  • 1
    its *very* different. boost shared_ptr will keep the object alive as long as somebody points at it, raw pointer or reference will not do that – pm100 Oct 04 '10 at 16:31
  • The problem is that this will generally run into a reference loop (the child points at the parent and the parent points at the child), which will result in the reference counts never dropping to 0 and the objects leaking when there are no other references to them. You may be able to avoid the problem by making one of the pointers a `weak_ptr`, but then you have to check it when you use it. – Chris Dodd Feb 08 '16 at 21:25
1

As mentioned by others, using std::reference_wrapper can be used. The helper functions std::ref() and std::cref() can be used, too. Unlike other postings, C++03 introduced reference_wrapper, ref() and cref() in the namespace std::tr1, so you have options if you're not using C++11 or beyond.

jestrada
  • 11
  • 1