Recently I have spotted one peculiar behaviour in direct Call to Destructor, that IMU worth of talking.
consider the following simple code:
#include <iostream>
class S {
public:
S() = default;
~S() { i=10; }
int i{100};
};
int main()
{
S s;
do {
std::cout << "Before foo: " << s.i;
s.~S();
std::cout << "; After: " << s.i << std::endl;
} while (false);
return 0;
}
Running:
$g++ -O3 ./d.cpp -o d
$./d
Before foo: 100; After: 0
$g++ -O0 ./d.cpp -o d
$./d
Before foo: 100; After: 10
IMU: this is very confusing. For some reason it seems like that direct call to destructor was optimized out, i.e. ignored. why?
I tried to appeal to gcc community, but was quite surprised with they reply: gcc-bug-103843
Their inability to understand the case, pushing off and jokes, make the environment contr-productive, I was not ready to continue.
I made some additional investigation:
#include <iostream>
class S {
public:
static constexpr size_t s_len=10;
public:
S() { for(auto i=0; i<s_len; ++i) m_arr[i]=10; }
~S() { for(auto i=0; i<s_len; ++i) m_arr[i]=11; /*prnt(); std::cout << std::endl;*/ }
void prnt() { for(auto i=0; i<s_len; ++i) std::cout << m_arr[i] << "; "; }
public:
int m_arr[s_len];
};
int main()
{
S s;
do {
std::cout << "Before foo: "; s.prnt();
s.~S();
std::cout << "; After: "; s.prnt();
std::cout << std::endl;
} while (false);
return 0;
}
Running:
$g++ -O0 ./d2.cpp -o d2
$./d2
Before foo: 10; 10; 10; 10; 10; 10; 10; 10; 10; 10; ; After: 11; 11; 11; 11; 11; 11; 11; 11; 11; 11;
$g++ -O3 ./d2.cpp -o d2
$./d2
Before foo: 10; 10; 10; 10; 10; 10; 10; 10; 10; 10; ; After: 10; 10; 10; 10; 10; 10; 10; 10; 10; 10;
This is even more confusing.
One more test:
#include <iostream>
class S {
public:
static constexpr size_t s_len=10;
public:
S() { for(auto i=0; i<s_len; ++i) m_arr[i]=10; }
~S() { for(auto i=0; i<s_len; ++i) m_arr[i]=11; prnt(); std::cout << std::endl; }
void prnt() { for(auto i=0; i<s_len; ++i) std::cout << m_arr[i] << "; "; }
public:
int m_arr[s_len];
};
int main()
{
S s;
do {
std::cout << "Before foo: "; s.prnt();
s.~S();
std::cout << "; After: "; s.prnt();
std::cout << std::endl;
} while (false);
return 0;
}
Running:
$g++ -O0 ./d2.cpp -o d2
$./d2
Before foo: 10; 10; 10; 10; 10; 10; 10; 10; 10; 10; 11; 11; 11; 11; 11; 11; 11; 11; 11; 11;
; After: 11; 11; 11; 11; 11; 11; 11; 11; 11; 11;
11; 11; 11; 11; 11; 11; 11; 11; 11; 11;
$g++ -O3 ./d2.cpp -o d2
$./d2
Before foo: 10; 10; 10; 10; 10; 10; 10; 10; 10; 10; 11; 11; 11; 11; 11; 11; 11; 11; 11; 11;
; After: 11; 11; 11; 11; 11; 11; 11; 11; 11; 11;
11; 11; 11; 11; 11; 11; 11; 11; 11; 11;
This is perfectly correct.
From what I see the direct call to destructor is not trivial and has some agreements. In same cases (-O3) it behaves differently, in addition it differs from one version compiler to the other.
I would like to better understand what these agreements are? Are they reliable? Who can help?
g++ --version
g++ (GCC) 10.2.1 20210130 (Red Hat 10.2.1-11)
Thnx in advance George