调用基本构造函数时是否应该复制继承构造函数的参数?

Should args to inherited constructors be copied when invoking the base ctor or not?

对于以下程序:

#include <iostream>

struct Foo
{
    Foo() { std::cout << "Foo()\n"; }
    Foo(const Foo&) { std::cout << "Foo(const Foo&)\n"; }
    ~Foo() { std::cout << "~Foo()\n"; }
};

struct A
{
    A(Foo) {}
};

struct B : A
{
    using A::A;
};

int main()
{
    Foo f;
    B b(f);
}

GCC 给出:

$ g++ -std=c++17 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
Foo()
Foo(const Foo&)
~Foo()
~Foo()

VS 2017(也在 C++17 模式下)给出:

Foo()
Foo(const Foo&)
Foo(const Foo&)
~Foo()
~Foo()
~Foo()

谁是对的,为什么?

(我们也不要忘记 VS 2017 没有正确执行强制复制省略。所以它可能只是副本是 "real" 但 GCC 根据 C++17 省略了它VS 没有的规则...)

尽管有省略,但在我看来 Visual Studio 是错误的:

[C++17: class.inhctor.init]/1: When a constructor for type B is invoked to initialize an object of a different type D (that is, when the constructor was inherited ([namespace.udecl])), initialization proceeds as if a defaulted default constructor were used to initialize the D object and each base class subobject from which the constructor was inherited, except that the B subobject is initialized by the invocation of the inherited constructor. The complete initialization is considered to be a single function call; in particular, the initialization of the inherited constructor's parameters is sequenced before the initialization of any part of the D object.

Visual Studio 似乎还没有实现 P0136。正确的 C++17 行为是一个副本,原来的 C++14 行为是两个副本。


C++14 规则 (N4140:[class.inhctor]) 将解释:

struct B : A
{
    using A::A;
};

如:

struct B : A
{
    B(Foo f) : A(f) { }
};

引入的构造函数在 p3 中指定,mem-initializer 在 p8 中等价。因此你得到了 Foo 的两个副本:一个进入 B 的合成构造函数,一个进入 A 的真实构造函数。


作为 P0136 的结果,C++17 规则非常不同 (N4659:[class.inhtor.init]):在那里,我们直接调用 A 的构造函数。这不像我们在向 B 添加新的构造函数 - 而且它不是一种可以用语言表达的机制。因为我们直接调用 A(Foo),那只是一个副本而不是两个。