1

I have just started using std::variant in my projects. I have a doubt. What will the destructor of std::variant do in the code shown below. Variant holds a void* data. Once variant goes out of scope, I think it will only free the memory of void* but not the actual object the pointer was pointing to. So there will be memory leak in this case. I would like to know if my understanding is correct or not.

#include <iostream>
#include <memory>
#include <variant>
using namespace std;

class A {
    public:
    ~A(){
        cout<<"Destructor called"<<endl;
    }
};


int main() {
    std::variant<void*> data;
    A* b = new A();
    data = (void*)b;
    return 0;
}
Barry
  • 286,269
  • 29
  • 621
  • 977
raju
  • 191
  • 1
  • 3
  • 12
  • 5
    Yes, your understanding is correct. – HolyBlackCat Nov 18 '19 at 19:51
  • 1
    I don't see any way that holding a `void*` in a `std::variant` make sense. If you are actually using this, there is probably something wrong in how you use `std::variant`. Holding pointers in `std::variant` at all already seems unusual. And why are you asking about `void*` rather than `A*`? Just to be sure, the `A` object wouldn't be destroyed either if the variant held a `A*` to it. – walnut Nov 18 '19 at 20:01
  • 2
    A small correction (sorry for nitpicking): The destructor itself doesn't free the memory occupied by `void *`, since it's not stored in the heap. `void *` is stored in a member variable of the variant (one way or another), so memory occupied by it is freed when the memory occupied by the variant is freed. – HolyBlackCat Nov 18 '19 at 20:02
  • 1
    What does this have to do with `void*`? Any (raw) pointer would behave the same way. – Davis Herring Nov 19 '19 at 06:15
  • @DavisHerring Yes, you are right. – raju Nov 19 '19 at 09:45

2 Answers2

3

When the variant destructor fires, it will call the destructor for whatever type of item is stored in the variant at that point. If that’s a void*, then C++ will say “okay, I will clean up the void*, and since that’s a primitive type, that’s a no-op.” It won’t look at the void*, realize that it’s actually a pointer to an A, and then delete the pointer as though it’s an A*.

The comments have pointed out that it’s fairly unusual to use a variant of a void*. A void* means “I’m pointing at something, and it’s up to you as the user to keep track of what it is and do the appropriate casting and resource management.” A variant means “I’m holding one of the following actual things, and I want C++ to remember which one and to do the appropriate resource management for me.” You may want to rethink your design, as there might be an easier way to do whatever you’re aiming to do here.

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
1

You are correct. The only pointer owning classes in the standard library that actually does delete (or delete[]) on pointers are the smart pointers.

std::variant is supposed to support you to hold one object of any number of types and primarily not pointers to objects. If the variant contains pointers, it means that some other object owns the data and is responsible for deleting it.

A std::variant capable of holding only one type is rarely useful either. You can declare the variable as a normal variable of that type in that case.

Here's one example of using a std::variant capable of holding objects of two unrelated types, and destruction will happen as expected.

#include <iostream>
#include <variant>

class A {
public:
    ~A() { std::cout << "A destructor called\n"; }
};

class B {
public:
    B() {}
    B(const B&) = default;
    B& operator=(const B&) = default;
    ~B() { std::cout << "B destructor called\n"; }
};

int main() {
    std::variant<A, B> data; // now holds a default constructed A
    data = B();              // deletes the A and now holds a default constructed B
    std::cout << "---\n";
}

Output:

A destructor called  // in "data = B()", the A must be destroyed
B destructor called  // the temporary B used in "data = B()"
---
B destructor called  // the B in the variant when the variant goes out of scope
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108