为什么在 C++17 中复制初始化需要析构函数并保证移动/复制省略?

JVA*_*pen 10 c++ language-lawyer copy-initialization copy-elision c++17

以下代码使用 MSVC (/permissive-) 编译,但无法使用 GCC/Clang 编译 m_ptr1 和 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;
}
Run Code Online (Sandbox Code Playgroud)

编译器资源管理器中的代码

我的理解是该=符号会导致复制初始化,但是,由于复制省略,我预计m_ptr2仍会被初始化而不会失败。

为什么m_ptr2需要 ForwardDeclared 的析构函数并且 Clang/GCC 对此是否正确?(奖励:得出 m_ptr1 被 MSVC 错误接受的结论是否正确?)

Fed*_*dor 1

我的理解是 = 符号会导致复制初始化,但是,由于复制省略,我预计 m_ptr2 仍会被初始化而不会失败。

复制省略要求类型的析构函数可访问且不可删除,即使没有对象被销毁,请参阅https://en.cppreference.com/w/cpp/language/copy_elision

所以GCC和Clang正确地检查了析构函数,这对于不完整的类型是无效的ForwardDeclared

额外奖励:m_ptr1 被 MSVC 错误接受的结论是否正确?

是的,MSVC 这里是不正确的。

请参阅为什么 C++ 中的强制 RVO 需要公共析构函数?了解为什么强制复制省略不适用的解释。

为什么 m_ptr2 需要 ForwardDeclared 的析构函数,Clang/GCC 对此是否正确?

同样的推理也适用于复制省略的有效析构函数的必要性。