The following code compiles with MSVC (/permissive-) and fails to compile with GCC/Clang for m_ptr1 and m_ptr2.
#include <memory>
struct ForwardDeclared;
class A {
public:
explicit A();
~A();
private:
std::unique_ptr<ForwardDeclared> m_ptr1 = nullptr; // not ok
std::unique_ptr<ForwardDeclared> m_ptr2 {std::unique_ptr<ForwardDeclared>{}}; // not ok
std::unique_ptr<ForwardDeclared> m_ptr3 {nullptr}; // ok
std::unique_ptr<ForwardDeclared> m_ptr4; // ok
};
int main() {
A a;
return 0;
}
My understanding is that the = sign results in copy initialization, however, thanks to copy elision I would expect m_ptr2 would still be initialized without failure.
Why does m_ptr2 require a destructor of ForwardDeclared and are Clang/GCC correct for this? (Bonus: Is it correct to conclude that m_ptr1 is incorrectly accepted by MSVC?)
EDIT: Logged a bug with clang about this issue: https://github.com/llvm/llvm-project/issues/54291
Copy elision requires the destructor of the type be accessible and non-deleted, even though no object is destroyed, see https://en.cppreference.com/w/cpp/language/copy_elision
So GCC and Clang correctly check the destructor, which is not valid for the incomplete type
ForwardDeclared.Yes, MSVC is incorrect here.
See Why is public destructor necessary for mandatory RVO in C++? for an explanation on why mandatory copy-elision doesn't apply.
The same reasoning applies here about the necessity of valid destructor for the copy elision.