pimpl、std::unique_ptr 和 constexpr 构造函数

Oer*_*ted 2 c++ pimpl-idiom unique-ptr constexpr

我正在审查一个非编译代码,其中我发现了与此类似的设计:

巴赫

#include <memory>

class A;

class B {
   private:
    int val;
    // pImpl idiom
    std::unique_ptr<A> pImpl;
    constexpr B(int x): val(x){};
    virtual ~B();
};
Run Code Online (Sandbox Code Playgroud)

析构函数是在 中定义的B.cpp,但构造函数是constexpr它意味着它是在 中定义的B.h。但是编译失败,因为编译器需要有一个 的构造函数A,此时它是一个不完整的类型。但我认为这constexpr是一个设计错误,因为我看不到如何B在编译时使用实现来构造 a 。

因此,constexpr在这种情况下是错误的,还是有办法B在编译时构造 a (我不认为std::unique_ptr可以在编译时构造,除了 from nullptr)?

注意我试图将构造函数定义推入内部,B.cpp但是链接器然后(从逻辑上我认为)触发了构造函数上的未定义引用...
注意编译到目前为止仅在 msvc 上进行了测试注意
我读了一堆关于 pimpl 的帖子并且unique_ptr(它们是很多)但我可能错过了一个足够的问题,而且问题很可能是重复的......

Art*_*yer 7

这不是 的问题constexpr。当您构造类的成员时,需要每个成员的析构函数可用,因为如果后续成员的构造函数抛出异常或构造函数主体抛出异常,则需要调用析构函数。

所以你不能使用,default_delete<A>因为类不完整。这就是为什么构造函数通常用pimpl在源文件中实现。最简单的解决方法是使用原始指针A* pImpl并记住在析构函数中将其删除。或者使用不同的删除器:

class B {
   private:
    static void delete_a(A* p) noexcept;
    struct a_deleter {
        void operator()(A* p) const noexcept { delete_a(p); }
    };
    int val;
    // pImpl idiom
    std::unique_ptr<A, a_deleter> pImpl;
    constexpr B(int x): val(x){};
    virtual ~B() = default;
};
Run Code Online (Sandbox Code Playgroud)
// Source/implementation file

class A {
    // ...
};

void B::delete_a(A* p) noexcept {
    delete p;
}
Run Code Online (Sandbox Code Playgroud)