I have been writing my own queue in c++.The problem is that it has a function called Queue::Pop() which calls the destructor of the first item in the queue which is kept track of by an index called _First, and afterwards _First is incremented and _Length is decremented.When normally compiled, it performs as expected, however when -O1 flag is added, it starts acting weird and does not call the object's destructor properly.The printing in destructor happens but the Id is not set to -1.and as result, when the queue goes out of the scope, the memory is delete[] ed and destructors are called again and that is invalid code since if it was a file descriptor, it would be closed twice.here is the Pop function:
#include <iostream>#include <tuple>#include <memory>#include <sys/uio.h>#include <initializer_list>namespace Core{ namespace Iterable { template <typename T> class Span { private: size_t _Length = 0; T *_Content = nullptr; inline T &_ElementAt(size_t Index) { return this->_Content[Index]; } inline const T &_ElementAt(size_t Index) const { return this->_Content[Index]; } public: Span() = default; Span(size_t Size) : _Length(Size), _Content(new T[Size]) {} Span(size_t Size, const T &Value) : _Length(Size), _Content(new T[Size]) { for (size_t i = 0; i < _Length; i++) { _Content[i] = Value; } } Span(Span &&Other) : _Length(Other._Length), _Content(Other._Content) { Other._Content = nullptr; Other._Length = 0; } Span(const Span &Other) : _Length(Other._Length), _Content(new T[Other._Length]) { for (size_t i = 0; i < Other._Length; i++) { _Content[i] = Other._Content[i]; } } Span(const T *Array, size_t Size) : _Length(Size), _Content(new T[Size]) { for (size_t i = 0; i < Size; i++) { _Content[i] = Array[i]; } } Span(std::initializer_list<T> list) : _Length(list.size()), _Content(new T[list.size()]) { size_t i = 0; for (auto &item : list) { _Content[i] = item; i++; } } ~Span() { delete[] _Content; _Content = nullptr; } inline T *Content() { return _Content; } inline const T *Content() const { return _Content; } inline size_t Length() const { return _Length; } T &operator[](const size_t &Index) { if (Index >= _Length) throw std::out_of_range(""); return _ElementAt(Index); } const T &operator[](const size_t &Index) const { if (Index >= _Length) throw std::out_of_range(""); return _ElementAt(Index); } Span &operator=(const Span &Other) { if (this != &Other) { _Length = Other._Length; delete[] _Content; _Content = new T[_Length]; for (size_t i = 0; i < _Length; i++) { _Content[i] = Other._Content[i]; } } return *this; } Span &operator=(Span &&Other) { if (this != &Other) { delete[] _Content; _Content = Other._Content; _Length = Other._Length; Other._Content = nullptr; Other._Length = 0; } return *this; } }; template <typename T> class BQueue final { public: // Constructors BQueue() = default; BQueue(size_t Size, bool Growable = true) : _Content(Size), _First(0), _Length(0), _Growable(Growable) {} BQueue(std::initializer_list<T> list) : _Content(list), _First(0), _Length(list.size()), _Growable(true) {} BQueue(const BQueue &Other) : _Content(Other._Content), _First(Other._First), _Length(Other._Length), _Growable(Other._Growable) {} BQueue(BQueue &&Other) : _Content(Other._Content), _First(Other._First), _Length(Other._Length), _Growable(Other._Growable) { Other._First = 0; Other._Length = 0; Other._Growable = true; } // Operators BQueue &operator=(const BQueue &Other) { if (this != &Other) { _Content = Other._Content; _First = Other._First; _Length = Other._Length; _Growable = Other._Growable; } return *this; } BQueue &operator=(BQueue &&Other) { if (this != &Other) { _Content = std::move(Other._Content); _First = std::move(Other._First); _Length = std::move(Other._Length); _Growable = std::move(Other._Growable); Other._First = 0; Other._Length = 0; Other.Growable = true; } return *this; } T &operator[](size_t Index) { if (Index >= _Length) throw std::out_of_range("Index out of range"); return _Content.Content()[(_First + Index) % Capacity()]; } T const &operator[](size_t Index) const { if (Index >= _Length) throw std::out_of_range("Index out of range"); return _Content.Content()[(_First + Index) % Capacity()]; } // Peroperties size_t Capacity() const { return _Content.Length(); } size_t Length() const { return _Length; } bool Growable() const { return _Growable; } T *Content() { return _Content.Content(); } T const *Content() const { return _Content.Content(); } inline bool IsWrapped() const { return _First + _Length > Capacity(); } inline bool IsEmpty() noexcept { return _Length == 0; } inline bool IsFull() noexcept { return _Length == Capacity(); } inline size_t IsFree() noexcept { return Capacity() - _Length; } // Helper functions T &Head() { AssertNotEmpty(); return _Content.Content()[_First]; } T const &Head() const { AssertNotEmpty(); return _Content.Content()[_First]; } // Remove functionality void Pop() { std::destroy_at(std::addressof(Head())); --_Length; _First = (_First + 1) % Capacity(); } private: Iterable::Span<T> _Content; size_t _First = 0; size_t _Length = 0; bool _Growable = true; inline void AssertNotEmpty() { if (IsEmpty()) throw std::out_of_range("Instance is empty"); } }; }}class Messenger{public: int Id = -1; Messenger() = default; Messenger(size_t id) : Id(id) { std::cout << Id << " Constructed" << std::endl; } Messenger(Messenger &&Other) : Id(Other.Id) { Other.Id = -1; } Messenger(Messenger const &Other) : Id(Other.Id) {} Messenger &operator=(Messenger &&Other) { Id = Other.Id; Other.Id = -1; return *this; } Messenger &operator=(Messenger const &Other) { Id = Other.Id; return *this; } ~Messenger() { if (Id != -1) { std::cout << Id << " Destructed" << std::endl; Id = -1; } }};using namespace Core;int main(int argc, char const *argv[]){ Iterable::BQueue<Messenger> Queue{1, 2}; Queue.Pop(); Queue.Pop(); std::cout << "Finished" << std::endl; return 0;}
And here the output that i get:
1 Constructed2 Constructed1 Destructed2 DestructedFinished2 Destructed1 Destructed
As you can see, after printing Finished, nothing else must be printed.