I have an exceptional situation where I need to call the destructor of a class to clear union memory. We can not use an std::variant yet. The class in the union is template based and defined similar to:
template<class TYPE>
class BaseTemplate
{
public:
BaseTemplate() = default;
~BaseTemplate() = default;
// Other useful functions.
private:
TYPE value;
}
Now we define different types with using:
using X = BaseTemplate<int>;
// Other using definitions
In an earlier situation X was a derived classes from BaseTemplate.
class X : public BaseTemplate<int>
{
X() = default;
~X() override = default; // In this case ~BaseTemplate was virtual.
// Nothing useful so we would like to remove this class.
};
In the old situation we were able to call the destructor like this:
X variableX;
variableX.~X();
In the new situation when using X = BaseTemplate<int>; is used this results in the error: expected class-name before ‘(’ token. So how do I call the destructor in this case?
Reproduction code:
#include <iostream>
namespace a
{
class Base
{
public:
Base() = default;
virtual ~Base() = default;
virtual void foo() = 0;
};
template<class TYPE>
class BaseTemplate : public Base
{
public:
BaseTemplate() = default;
~BaseTemplate() override = default;
// Other useful functions.
void set(const TYPE& v)
{
value = v;
}
TYPE get() const
{
return value;
}
void foo() final
{
value *= 2;
}
private:
TYPE value;
};
using X = BaseTemplate<int>;
using Y = BaseTemplate<unsigned int>;
using Z = BaseTemplate<float>;
} // End of namespace a
union XYZ
{
XYZ() {}
~XYZ() {}
a::X variableX;
a::Y variableY;
a::Z variableZ;
};
XYZ xyz;
int main()
{
// Inplace new operator to initialize x
new(&xyz.variableX) a::X;
xyz.variableX.set(1);
xyz.variableX.foo();
std::cout << "Result: " << xyz.variableX.get() << std::endl;
xyz.variableX.~X();
}
>Solution :
variableX‘s destructor is called ~BaseTemplate(). However, if X is in scope and resolves to BaseTemplate<int>, it will also work as an alias for the destructor.
If X was not in scope:
some::random_namespace::X variableX;
// variableX.~X(); X not in scope, will not work
variableX.some::random_namespace::X::~X(); // Works, but confusing
// variableX.~decltype(variableX)(); // Supposed to work, but GCC does not like
using T = decltype(variableX); variableX.~T();
The real solution is to use std::destroy_at(std::addressof(variableX))
std::destroy_at(&variableX); // Does not care about the name of the destructor