构造函数在私有继承类型之外可用?

Constructor is available outside of privately inheriting type?

标题只是我对以下示例感到困惑的几件事之一:

struct A {
    A() {
        std::cout << "Default constructor of A" << std::endl;
    }
    A(const A &) {
        std::cout << "Copy constructor of A" << std::endl;
    }
};

struct B : private A {
    // using A::A; // does not help for question 2.    
};

int main() {
    A a;
    B b;
    B c(b); // Does not work with `a` as an argument
    return 0;
}

这个例子输出:

Default constructor of A
Default constructor of A
Copy constructor of A

问题:

  1. 为什么 B 的私有继承构造函数在 main 中可用?这个问题类似于 post 中的问题,但问题是关于从 B 内部使用该构造函数,这是不同的。
  2. 被调用的复制构造函数接受一个 const A & 参数。但是,如果我写 B c(a) 而不是 B c(b),代码将无法编译。怎么来的? (请注意 un-commenting B 中的 using 指令没有帮助。
  3. 这是次要的,但仍然如此。为什么编译器不警告我未使用的变量 ac

问题 2 已转到另一个 post

How come the privately inherited constructors of B are available in main?

他们不是。

但是,它们可供 B 本身使用,因为它们已经被继承,并且是 B 调用继承的基本构造函数。

显然,如果不是这样,那么私有继承将毫无用处,因为您永远无法实例化私有继承 class.

They get called and the output is the proof of that

不,不是。你在妄下结论。有一些中间函数调用(B 中隐式定义的构造函数)不会产生您没有考虑到的输出。

The copy constructor that gets called takes an const A & argument. However, if I write B c(a) instead of B c(b), the code does not compile. How come?

不,它没有。 您的代码行调用了 B 中的复制构造函数,它接受一个 const B& 参数。 B 没有采用 const A& 参数的复制构造函数。

该复制构造函数又在内部调用基础的复制构造函数,产生您看到的输出。

构造函数并不是以您所相信的方式继承的。

This is minor, but still. How come the compiler does not warn me about unused variables a and c?

因为它们不是未使用。它们的构造做了一件事(创建输出)。

  1. 您没有直接调用 A 的构造函数,B 的默认构造函数正在为您执行此操作。如果这有任何不同,你将永远无法构造任何 class 来私下继承任何东西。

  2. 这是因为 B 没有类型 A 的复制构造函数。唯一可以在此处应用的构造函数是类型 B 的(默认)复制构造函数,它以 B 作为参数。

构造函数绑定到它们的 class,它们不像函数那样继承。如何解释... 基本目标是确保每个 class 始终完全构建。因此,要构造任何类型的 A(无论是独立的还是作为 B 的一部分),A 的构造函数必须 运行。类似地,要构造 class B 的对象,B 的构造函数必须 运行。如果您 'inherit' A::A() 构造函数,您将能够构造未完全构造的 B。在这种情况下,A::A() 构造函数没有 运行 B 构造序列的任何部分,使 B 处于无效状态。

让我们尝试一些不同的来源。我们保持A不变,把B改成这样:

struct B : private A {
    B () { val = 42; }
    void foo () { if (val != 42) abort (); }

    using A::A;

    int val;
};

现在假设我们,呃,获得了一个 B 而没有构建它:

B b (a); // illegal, but for the sake of argument.
b.foo ();

我们已经指定我们使用 A::A 构造函数构造此 B,因此将执行的唯一代码是 A::A()。特别是,B::B() 没有被执行,所以 val 将具有当时堆栈中存在的任何值。 42的概率是2^32分之一,换句话说,可能性不大。

调用 B.foo() 时会发生什么?该对象不处于有效状态(val 不是 42),因此应用程序中止。糟糕!

这当然是一个人为的例子,但它表明使用非构造对象是一件非常糟糕的事情,因此该语言会阻止您创建任何此类对象。

  1. 它们并非未使用。构造函数(写入 cout)中发生了各种 activity,不能简单地消除。

这种设计模式在 C++ 中多次出现,例如 std::mutex。只需声明一个锁就可以进行加锁和解锁,但声明后不需要再引用锁。由于它是一种常见的设计模式,因此警告是不合适的。

只有当编译器可以证明构造和析构局部变量没有副作用,并且发现它没有被使用时,您才可能看到警告(取决于编译器)。