Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

std::optional<T> appears to destroy T twice when leaving its scope. Why?

I’m looking to replace some old raw pointer handling for objects (unparented QObjects) that in practice are treated as optional objects.
E.g:

if(m_file)
   delete m_file;
m_file = new QFile(...);`

Both std::unique_ptr and std::optional seem to be suitable for my needs.

I decided to do some testing first, and came across some strange behaviour with std::optional: The T::dtor appears to be called twice when exiting scope.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

https://godbolt.org/z/Yeq887YPh.

struct Loud
{
    Loud(std::string name) : m_name{ name } { print("Creating"); }
    Loud(const Loud& other) : m_name{ other.m_name } { print("Copying"); }
    Loud(Loud&& other) : m_name{ other.m_name }{ print("Moving"); }
    ~Loud() { print("Destroying"); }
    Loud& operator=(const Loud& other){ this->m_name = other.m_name; print("Copy="); return *this;}
    Loud& operator=(Loud&& other){ this->m_name = other.m_name; print("Move=");; return *this;}

    std::string m_name;
    void print(std::string operation) { std::cout << operation << " " << m_name << "\n"; }
};

void optionalTest()
{
    std::optional<Loud> opt;
    opt = Loud("opt 1");
    opt = Loud("opt 2");
}

void uniqueTest()
{
    std::unique_ptr<Loud> unique;
    unique = std::make_unique<Loud>("unique 1");
    unique =  std::make_unique<Loud>("unique 2");
}

int main()
{
    optionalTest();
    std::cout << "\n";
    uniqueTest();
}

Which produces the output

Creating opt 1
Moving opt 1
Destroying opt 1
Creating opt 2
Move= opt 2
Destroying opt 2
Destroying opt 2    <-- Whyy??

Creating unique 1
Creating unique 2
Destroying unique 1
Destroying unique 2

I’m a bit worried that the double dtor might cause issues with deallocating resources, so I’m currently more inclined to opt for the unique_ptr.

>Solution :

Everything is fine. You have 3 objects being worked on:

a) in std::optional,
b) temporary opt 1 and
c) temporary opt2.

Now, referring those to your output:

Creating opt 1   // create b
Moving opt 1     // create a using b
Destroying opt 1 // destroy b
Creating opt 2   // create c
Move= opt 2      // update a using c
Destroying opt 2 // destroy c
Destroying opt 2 // destroy a

std::unique_ptr doesn’t store its own object, only a pointer, so there’s no 3rd object in this case.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading