Error when deleting object holding reference

I have two classes. Class Root and class Other. An object of Root is created and this object holds an array where you can store objects of class Other. Root object creates a Other object inside this array and gives a reference to it to the new objects constructor. Now when I try to store this array inside a reference in object Other and then try to delete object Other, it won't compile. I thought this might be, because object Other tries to deallocate an array that has originally been allocated by object Root. Here the source code, that throws the error:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <vector>
#include <iostream>

class Other {
public:
    Other(std::vector<Other> &gotten) : ref(gotten) {
        std::cout << "Constructed other" << std::endl;
        kill();
    }

    void kill() {
        auto it = ref.begin();
        ref.erase(it);
    }

private:
    std::vector<Other> &ref;
};

class Root {
public:
    Root() {
        std::cout << "Constructed Root" << std::endl;
        storage.emplace_back(storage);
    }

private:
    std::vector<Other> storage;
};

int main() {
    Root r;
}


When I tried rewrite the same code by using pointer, it works. I think that is so, because when deleting object Other, it only deallocates the pointer, and not the entire array as when using references.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <vector>
#include <iostream>

class Other {
public:
    Other(std::vector<Other> &gotten) : ref(&gotten) {
        std::cout << "Constructed other" << std::endl;
        kill();
    }

    void kill() {
        auto it = ref->begin();
        ref->erase(it);
    }

private:
    std::vector<Other> *ref;
};

class Root {
public:
    Root() {
        std::cout << "Constructed Root" << std::endl;
        storage.emplace_back(storage);
    }

private:
    std::vector<Other> storage;
};

int main() {
    Root r;
}


As said in the beginning of this post, the only reason I could think of, why the compiler throws these errors, is because when using references I try to delete something, that the object Other does not own. But there is a good chance, that I am wrong. Can someone explain it better?
Last edited on
> it won't compile.
in those cases, the compiler gives you an error message that describes the issue with your code
post your compiler message verbatim

> I thought this might be, because object Other tries to deallocate an array
> that has originally been allocated by object Root.
> (...)
> the only reason I could think of, why the compiler throws these errors, is
> because when using references I try to delete something, that the object
> Other does not own.
no, luckily the compiler is not so «smart»
also, ¿do you realise that ref.erase(ref.begin()); only removes the first element?
¿what deallocation are you talking about?
This is the compiler error message:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/usr/include/c++/10.2.0/bits/stl_algobase.h: In instantiation of ‘static constexpr _OI std::__copy_move<true, false, std::random_access_iterator_tag>::__copy_m(_II, _II, _OI) [with _II = Other*; _OI = Other*]’:
/usr/include/c++/10.2.0/bits/stl_algobase.h:469:12:   required from ‘constexpr _OI std::__copy_move_a2(_II, _II, _OI) [with bool _IsMove = true; _II = Other*; _OI = Other*]’
/usr/include/c++/10.2.0/bits/stl_algobase.h:506:42:   required from ‘constexpr _OI std::__copy_move_a1(_II, _II, _OI) [with bool _IsMove = true; _II = Other*; _OI = Other*]’
/usr/include/c++/10.2.0/bits/stl_algobase.h:514:31:   required from ‘constexpr _OI std::__copy_move_a(_II, _II, _OI) [with bool _IsMove = true; _II = __gnu_cxx::__normal_iterator<Other*, std::vector<Other> >; _OI = __gnu_cxx::__normal_iterator<Other*, std::vector<Other> >]’
/usr/include/c++/10.2.0/bits/stl_algobase.h:601:38:   required from ‘constexpr _OI std::move(_II, _II, _OI) [with _II = __gnu_cxx::__normal_iterator<Other*, std::vector<Other> >; _OI = __gnu_cxx::__normal_iterator<Other*, std::vector<Other> >]’
/usr/include/c++/10.2.0/bits/vector.tcc:175:2:   required from ‘std::vector<_Tp, _Alloc>::iterator std::vector<_Tp, _Alloc>::_M_erase(std::vector<_Tp, _Alloc>::iterator) [with _Tp = Other; _Alloc = std::allocator<Other>; std::vector<_Tp, _Alloc>::iterator = std::vector<Other>::iterator]’
/usr/include/c++/10.2.0/bits/stl_vector.h:1431:24:   required from ‘std::vector<_Tp, _Alloc>::iterator std::vector<_Tp, _Alloc>::erase(std::vector<_Tp, _Alloc>::const_iterator) [with _Tp = Other; _Alloc = std::allocator<Other>; std::vector<_Tp, _Alloc>::iterator = std::vector<Other>::iterator; std::vector<_Tp, _Alloc>::const_iterator = std::vector<Other>::const_iterator]’
/home/user/CLionProjects/Reverb/main.cpp:13:21:   required from here
/usr/include/c++/10.2.0/bits/stl_algobase.h:400:18: error: use of deleted function ‘Other& Other::operator=(Other&&)’
  400 |        *__result = std::move(*__first);
      |        ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~
/home/user/CLionProjects/Reverb/main.cpp:4:7: note: ‘Other& Other::operator=(Other&&)’ is implicitly deleted because the default definition would be ill-formed:
    4 | class Other {
      |       ^~~~~
/home/user/CLionProjects/Reverb/main.cpp:4:7: error: non-static reference member ‘std::vector<Other>& Other::ref’, cannot use default assignment operator
In file included from /usr/include/c++/10.2.0/vector:60,
                 from /home/user/CLionProjects/Reverb/main.cpp:1:
/usr/include/c++/10.2.0/bits/stl_algobase.h: In instantiation of ‘static constexpr _Tp* std::__copy_move<_IsMove, true, std::random_access_iterator_tag>::__copy_m(const _Tp*, const _Tp*, _Tp*) [with _Tp = Other; bool _IsMove = true]’:
/usr/include/c++/10.2.0/bits/stl_algobase.h:472:30:   required from ‘constexpr _OI std::__copy_move_a2(_II, _II, _OI) [with bool _IsMove = true; _II = Other*; _OI = Other*]’
/usr/include/c++/10.2.0/bits/stl_algobase.h:506:42:   required from ‘constexpr _OI std::__copy_move_a1(_II, _II, _OI) [with bool _IsMove = true; _II = Other*; _OI = Other*]’
/usr/include/c++/10.2.0/bits/stl_algobase.h:514:31:   required from ‘constexpr _OI std::__copy_move_a(_II, _II, _OI) [with bool _IsMove = true; _II = __gnu_cxx::__normal_iterator<Other*, std::vector<Other> >; _OI = __gnu_cxx::__normal_iterator<Other*, std::vector<Other> >]’
/usr/include/c++/10.2.0/bits/stl_algobase.h:601:38:   required from ‘constexpr _OI std::move(_II, _II, _OI) [with _II = __gnu_cxx::__normal_iterator<Other*, std::vector<Other> >; _OI = __gnu_cxx::__normal_iterator<Other*, std::vector<Other> >]’
/usr/include/c++/10.2.0/bits/vector.tcc:175:2:   required from ‘std::vector<_Tp, _Alloc>::iterator std::vector<_Tp, _Alloc>::_M_erase(std::vector<_Tp, _Alloc>::iterator) [with _Tp = Other; _Alloc = std::allocator<Other>; std::vector<_Tp, _Alloc>::iterator = std::vector<Other>::iterator]’
/usr/include/c++/10.2.0/bits/stl_vector.h:1431:24:   required from ‘std::vector<_Tp, _Alloc>::iterator std::vector<_Tp, _Alloc>::erase(std::vector<_Tp, _Alloc>::const_iterator) [with _Tp = Other; _Alloc = std::allocator<Other>; std::vector<_Tp, _Alloc>::iterator = std::vector<Other>::iterator; std::vector<_Tp, _Alloc>::const_iterator = std::vector<Other>::const_iterator]’
/home/user/CLionProjects/Reverb/main.cpp:13:21:   required from here
/usr/include/c++/10.2.0/bits/stl_algobase.h:422:39: error: static assertion failed: type is not assignable
  422 |    static_assert( __assignable::type::value, "type is not assignable" );
      |                                       ^~~~~


The deallocation I am talking about is the one, when Other object is deleted. Basically all variables allocated inside this object are deallocated, right [memory addresses are freed]? So because there is a reference to the vector that has been declared in Root object, it could be problematic when the Other object is deleted because the memory of that vector is also deallocated. And it is okay that it only removes the first element of the vector, as there is only one element in it, the Other object.
Last edited on
The first code using ref needs a copy assignment for Other.

Last edited on
But why? I am not copying Other class.
I tried it with VS and I got different error messages - but it reported implicitly deleted operator=() which is also included within the error messages shown above.

I tried:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <vector>
#include <iostream>

class Other {
public:
	Other(std::vector<Other>& gotten) : ref(&gotten) {
		std::cout << "Constructed other" << std::endl;
		kill();
	}

	void kill() {
		auto it = ref->begin();
		ref->erase(it);
	}

	Other& operator=(const Other&) { return *this; }

private:
	std::vector<Other>* ref;
};

class Root {
public:
	Root() {
		std::cout << "Constructed Root" << std::endl;
		storage.emplace_back(storage);
	}

private:
	std::vector<Other> storage;
};

int main() {
	Root r;
}


which compiles OK with VS2019 (note body of operator=() is just to get it compile).
> But why? I am not copying Other class.
you are calling .erase() into a vector of Other
1
2
3
for(int K=p; K<size-1; ++K)
   v[K] = v[K+1]; // here is trying to use the assignment operator
--size;

a reference is not copyable/assignable so the compiler complains

may also need copy/move constructor, as the vector may reallocate when its capacity is exceeded.


> when the Other object is deleted because the memory of that vector is also deallocated.
not how it works
1
2
3
4
5
6
7
8
9
10
11
12
int a;
{
   int &ref_a = a;
} //ref_a dies here
a = 42; //valid

int& foo(){
   int n=42;
   return n;
}
int &ref_n = foo();
ref_n = 42; // shooting your foot 
references are not smart pointers


> note body of operator=() is just to get it compile
you changed `ref' to a pointer, there is an operator= provided by the compiler, no need for a custom one to make it compile
So what would be the best way to solve this problem? Is it generally better to store pointers instead of references inside of objects like in this case?
Basically you are saying that because I delete the object from the vector list, it tries to assign the reference inside the Other object to another memory address, which fails, because references cannot be reassigned.
Why does this code work then, I am doing the same, right?
1
2
3
4
5
6
7
8
9
10
#include <vector>
#include <iostream>
int main() {
    std::vector<int> root;
    root.emplace_back(1);
    root.emplace_back(2);
    std::vector<int> &ref = root;
    std::vector<int>::iterator it = ref.begin();
    ref.erase(it);
}
Last edited on
not the same: vector<int> vs vector<Other>


read the compiler messages:
> error: use of deleted function ‘Other& Other::operator=(Other&&)
> ‘Other& Other::operator=(Other&&)’ is implicitly deleted because the default definition would be ill-formed:
> reference member ‘std::vector<Other>& Other::ref’, cannot use default assignment operator
there is no assignment operator, you can't do
1
2
Other a, b;
a = b;

¿why? because one of your members is a reference

> required from ‘std::vector::iterator std::vector::erase(std::vector::const_iterator)
¿who is trying to use the assignment operator? it's vector::erase()
(the rest are implementation details, you may see that it tries to call a move() function)


So what I'm saying is:
.erase() requieres your class to have an operator=,
the compiler cannnot provide your class an operator= because one of its members is a reference

¿what can you do? provide a proper operator= or don't have a reference as a member


> Is it generally better to store pointers instead of references inside of
> objects like in this case?
code has a porpuse
I have no idea what problem you are trying to solve, so cannot suggest a solution
¿you want the best way to seppuku a newborn?
Topic archived. No new replies allowed.