0

I came across the following problem, with two classes X and Y where Y has a member of type X

If I then:

  • create a vector of objects of type Y via std::vector<Y> list = {{}}
  • and Y has a defaulted default constructor

One of the instances of type X will be destroyed twice. Even when using rule of three (or five).

It does not happen, if I either create the vector via {Y{}} or define a user supplied default constructor for Y.

See the console output of the following example:

#include <iostream>
#include <vector>

class X
{
public:
    X()
    {
        std::cout << "constructed " << this << std::endl;
    }
    ~X()
    {
        std::cout << "deleted " << this << std::endl;
    }
    X(const X& other)
    {
        std::cout << "copy constructed " << &other << " to " << this << std::endl;
    }
};

class Y
{
public:
    //Y(){}; // works
    Y() = default; // doesn't work
    ~Y() = default;
    Y(const Y& y) = default; // copy constructor
    Y& operator=(const Y& y) = default; // copy assignment
    // Y(Y&& other) noexcept = default; // move constructor
    // Y& operator=(Y&& other) noexcept = default; // move assignment
private:
    X x;
};

int main()
{
    std::cout << "start" << std::endl;
    std::vector<Y> list = {{}}; // doesn't work
    //std::vector<Y> list = {Y{}}; // works
    std::cout << "done" << std::endl;
    return 0;
}

Outputs

start
constructed 000000271CAFFCD0
copy constructed 000000271CAFFCD0 to 000001727F7145B0
deleted 000000271CAFFCD0
deleted 000000271CAFFCD0
done
deleted 000001727F7145B0

https://godbolt.org/z/vscP4TexE

This is only reproducable in MSVC.

Am I missing something or is this a compiler bug?

Edit

Thanks for the hint about the missing return statements. I added them, but get the same results.

I did enable warnings, but it only shows that the unreferenced inline functions are removed.

Edit 2

Since they are unused, I removed copy and move constructors and assignment operators from the example.

Edit 3

Adapted Output to the new code example. It still contains the double delete. Note that MSVC is a bit flakey on godbolt and it sometimes does not compile the example.

  • 1
    You're missing a return statement in `X::operator=`. So **undefined behavior**. See [Why should I always enable compiler warnings?](https://stackoverflow.com/questions/57842756/why-should-i-always-enable-compiler-warnings) – Jason Dec 09 '22 at 07:29
  • Thanks for pointing that out. It turned out, that those operators were not referenced and removed before even triggering a compiler warning. – Michael Schmitt Dec 09 '22 at 19:45
  • 1
    I have extended your code https://godbolt.org/z/dMeT3Pcex Can't build there with `/fsanitize=address`. If there is no `p = nullptr`, double free is possible. VS possible bug. Please report it to MS. I suppose a possible bug with improperly used the placement new. – 273K Dec 09 '22 at 22:32
  • Thank You :) I started with a similar example - maybe this is the most consice one: https://godbolt.org/z/vP8c4fssP (using a unique_ptr and crashing). – Michael Schmitt Dec 10 '22 at 07:18
  • You can report the problem [Developer Community](https://developercommunity.visualstudio.com/search?space=62) and post link here. – Minxin Yu - MSFT Dec 12 '22 at 02:35
  • I reported the problem here: https://developercommunity.visualstudio.com/t/Double-destroy-when-using-compiler-gener/10227826 – Michael Schmitt Dec 12 '22 at 10:28

0 Answers0