调用基本构造函数时是否应该复制继承构造函数的参数?
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)
,那只是一个副本而不是两个。
对于以下程序:
#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 typeB
is invoked to initialize an object of a different typeD
(that is, when the constructor was inherited ([namespace.udecl])), initialization proceeds as if a defaulted default constructor were used to initialize theD
object and each base class subobject from which the constructor was inherited, except that theB
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 theD
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)
,那只是一个副本而不是两个。