I was testing my own RAII pointer implementation that does some weird stuff (by design). To test it, I made a class that tracks constructors and destructors and makes sure everything is deleted and created exactly once.
But I was constantly getting an error that I'm deleting something twice. Weird, right. I found out why and you can see it by running the sample below.
This is not the code that originally produced it, but it demonstrates the issue.
struct ReportDelete
{
ReportDelete() = delete;
ReportDelete(const std::string& name) : name(name) { std::cout << "create " << name << "\n"; }
ReportDelete(ReportDelete&& moveHere) : name(std::move(moveHere.name)) {}
ReportDelete& operator=(ReportDelete&& moveHere)
{
name = std::move(moveHere.name);
return *this;
}
ReportDelete(const ReportDelete&) = delete;
void operator=(const ReportDelete&) = delete;
std::string name;
~ReportDelete()
{
std::cout << "delete " << name << "\n";
name = name "-deleted";
}
};
int main(int argc, const char** argv)
{
std::vector<ReportDelete> moveTest;
moveTest.push_back(ReportDelete("test"));
}
This prints:
create test
delete
delete test
So the destructor is still called on the moved value. That does not do anything bad in particular, but it makes it impossible for me to test the create/delete ratio correctly.
My actual test class does this in the destructor:
virtual ~Value()
{
std::string error = name " deleted twice!";
assertm(parent.values[fullname()] == false, error.c_str());
parent.values[fullname()] = true;
}
Where parent is the class that tracks whether the values are deleted at the end. But how can I know here that this is only being run for Value&&
and should be disregarded?
I tried this:
Value~() &&
{
std::cout << "rvalue destructor ignored...\n";
}
But it seems you cannot have a special destructor like that.
How can I know that a destructor is being only called on an rvalue refference and that I can therefore ignore it and not track it as a real delete?
CodePudding user response:
This has nothing to do with a destructor getting called for an r-value reference, or not. A destructor is a destructor. An object is getting destroyed. The End. The particular details of the object are immaterial.
ReportDelete& operator=(ReportDelete&& moveHere)
{
name = std::move(moveHere.name);
return *this;
}
This will move name
from the moved-from instance of this object. However, the moved-from instance of the object is still a valid, existing object, and at some point it will be destroyed.
However you moved-from its name
member. Your C implementation's result of that is that the moved-from std::string
name is left to be an empty string.
And when that moved-from object gets destroyed its destructor prints an empty name. This is the output that you're seeing.
TLDR: you were not getting an error. What you overlooked is that a moved-from object is still a valid, existing object, and it is still subject to being destroyed. Which it will be, in a well-formed C program. In fact, because, in this case, the moved-from object is an rvalue reference that increases, quite significantly, the likelyhood that the moved-from object is about to get nuked from high orbit.