继承私有构造函数

Ami*_*rsh 4 c++ constructor c++11

试图允许make_unique使用私有 ctor 的类时,我发现两种情况之间存在以下奇怪的区别:


案例 1 - 空 ctor -编译

class A {
    int _i;
    A(): _i(7) {}
public:
    template<typename... T>
    static std::unique_ptr<A> create(T&&... t) {
        struct enablePrivateCtor : public A {
            using A::A;
        };
        return std::make_unique<enablePrivateCtor>(std::forward<T>(t)...);
    }
    void doIt() const {
        std::cout << _i << std::endl;
    }
};

int main() {
    auto a = A::create();
    a->doIt();
}
Run Code Online (Sandbox Code Playgroud)

输出:

7
Run Code Online (Sandbox Code Playgroud)

案例 2 - 非空构造函数-不编译

class A {
    int _i;
    A(int i): _i(i) {} // <- change 1, ctor getting int
public:
    // no change here!
    template<typename... T>
    static std::unique_ptr<A> create(T&&... t) {
        struct enablePrivateCtor : public A {
            using A::A;
        };
        return std::make_unique<enablePrivateCtor>(std::forward<T>(t)...);
    }
    void doIt() const {
        std::cout << _i << std::endl;
    }
};

int main() {
    auto a = A::create(7); // <- change 2, sending 7
    a->doIt();
}
Run Code Online (Sandbox Code Playgroud)

编译错误:

unique_ptr.h: error: calling a private constructor of class 'enablePrivateCtor'
Run Code Online (Sandbox Code Playgroud)

为什么第一个 - 带有空 ctor - 可以,而第二个 - 非空 ctor - 不是?

Nic*_*las 5

默认构造函数永远不会被继承。因此,第一个enablePrivateCtor生成默认构造函数,它调用基类默认构造函数。

当您继承构造函数时(如第二种情况),新构造函数与继承的构造函数具有相同的访问级别。既然A::A(int)是私有的,那么也将是私有的enablePrivateCtor::enablePrivateCtor(int)。所以你将无法用它构建。

如果您需要一个私有构造函数能够被间接调用(通过make_unique/ emplace/etc),那么您需要使用一个私有密钥类型。像这样:

class A;

class A_key
{
  A_key() = default;
  A_key(int) {} //Prevent `A_key` from being an aggregate.

  friend class A;
};

class A {
    int _i;
public:
    A(int i, A_key): _i(i) {}

    // no change here!
    template<typename... T>
    static std::unique_ptr<A> create(T&&... t)
    {
        return std::make_unique<A>(std::forward<T>(t)..., A_key{});
    }

    void doIt() const {
        std::cout << _i << std::endl;
    }
};

...

auto ptr = A::create(7);
A a(7, A_key{}); //Does not compile, since you're not a friend.
Run Code Online (Sandbox Code Playgroud)

A_key可公开复制,但不可公开默认构造。所以非私有代码可以传递它们,但非私有代码不能创建它们。

  • 对未来读者的注意:这种访问修饰符的继承是构造函数所特有的:“命名构造函数的 using 声明符不会创建同义词;相反,如果额外的构造函数在用于构造对象的对象时可访问,则它们是可访问的。相应的基类,并且忽略 using 声明的可访问性。” (2认同)