Consider the following code:
#include <memory>
class A {
protected:
~A() = default;
friend void std::destroy_at<A>(A*); // libstdc++ ok, but libc++ failed.
public:
void f() {
std::destroy_at(this);
}
};
int main() {
}
See online demo.
Note that for the code above, libstdc++ is ok, while libc++ is not.
The root cause is: libc++ implements std::destroy_at
as follows:
template<typename T, enable_if_t<!is_array_v<T>, int> = 0>
void destroy_at(T*) { ... }
However, the C++ standard actually defines std::destroy_at
as follows:
template<typename T>
void destroy_at(T*) { ... }
May the compiler add extra defaulted template parameters to the std
templates? Is it conforming to the standard?
By implementing a simple destroy_at
locally, one can see that friendship is granted:
template<typename T, std::enable_if_t<!std::is_array_v<T>, int> = 0>
void destroy_at(T* loc) { loc->~T(); }
The problem in this case with libc++ is that the destructor is not directly called from std::destroy_at
. Instead, std::destroy_at
calls a helper std::__destroy_at
. The helper is not a friend.