多副本构造函数继承中的惊人行为
Surprising behavior in multiple copy constructor inheritance
从 C++11 开始,可以有两个复制构造函数,一个接受 T&
类型的参数,一个接受 const T&
.
类型的参数
我有这样一种情况,当构造函数在派生的 class 中继承时,(表面上)添加第二个复制构造函数不会导致任何一个被调用。当两者都存在时,复制构造函数将被模板化构造函数覆盖。
这是一个 MWE:
struct A {
template <typename... Args>
A (Args&&... args)
{ std::cout << "non-default ctor called\n"; }
A (A&) { std::cout << "copy ctor from non-const ref\n"; }
};
struct C :public A { using A::A; };
int main() {
C c1;
C c2(c1);
}
运行这段代码,我们看输出
non-default ctor called
copy ctor from non-const ref
符合预期。
但是,向struct A
添加一个额外的构造函数如下:
A (const A&) { }
不知何故导致另一个复制构造函数未被调用,因此输出变为
non-default ctor called
non-default ctor called
在我的用例中,我想将所有构造函数从基础 class 继承到派生的 class,包括复制构造函数和其他任何东西。但似乎不知何故,当两个复制构造函数都存在时,它们不会被继承。这是怎么回事?
来自https://en.cppreference.com/w/cpp/language/using_declaration
If one of the inherited constructors of Base happens to have the signature that matches a copy/move constructor of the Derived, it does not prevent implicit generation of Derived copy/move constructor (which then hides the inherited version, similar to using operator=).
所以
struct C :public A { using A::A; };
是
struct C :public A
{
using A::A;
C(const C&) = default;
C(C&&) = default;
};
其中 C(const C&) = default;
类似于
C(const C& c) : A(static_cast<const A&>(c)) {}
所以
struct A {
template <typename... Args>
A (Args&&... args)
{ std::cout << "non-default ctor called\n"; }
A (A&) { std::cout << "copy ctor from non-const ref\n"; }
};
选择了模板构造函数,但是
struct A {
template <typename... Args>
A (Args&&... args)
{ std::cout << "non-default ctor called\n"; }
A (const A&) { std::cout << "copy ctor from const ref\n"; }
A (A&) { std::cout << "copy ctor from non-const ref\n"; }
};
A (const A&)
被选中。
如您所见,还有一个缺陷:
The semantics of inheriting constructors were retroactively changed by a defect report against C++11. Previously, an inheriting constructor declaration caused a set of synthesized constructor declarations to be injected into the derived class, which caused redundant argument copies/moves, had problematic interactions with some forms of SFINAE, and in some cases can be unimplementable on major ABIs. Older compilers may still implement the previous semantics.
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0136r1.html
有了那个缺陷,你的 class C 会是
struct C :public A
{
using A::A;
template <typename ...Ts>
C(Ts&&... ts) : A(std::forward<Ts>(ts)...) {} // Inherited.
C(const C&) = default;
C(C&&) = default;
};
所以你调用 C(C& c) : A(c) {}
(在模板替换之后)。
从 C++11 开始,可以有两个复制构造函数,一个接受 T&
类型的参数,一个接受 const T&
.
我有这样一种情况,当构造函数在派生的 class 中继承时,(表面上)添加第二个复制构造函数不会导致任何一个被调用。当两者都存在时,复制构造函数将被模板化构造函数覆盖。
这是一个 MWE:
struct A {
template <typename... Args>
A (Args&&... args)
{ std::cout << "non-default ctor called\n"; }
A (A&) { std::cout << "copy ctor from non-const ref\n"; }
};
struct C :public A { using A::A; };
int main() {
C c1;
C c2(c1);
}
运行这段代码,我们看输出
non-default ctor called
copy ctor from non-const ref
符合预期。
但是,向struct A
添加一个额外的构造函数如下:
A (const A&) { }
不知何故导致另一个复制构造函数未被调用,因此输出变为
non-default ctor called
non-default ctor called
在我的用例中,我想将所有构造函数从基础 class 继承到派生的 class,包括复制构造函数和其他任何东西。但似乎不知何故,当两个复制构造函数都存在时,它们不会被继承。这是怎么回事?
来自https://en.cppreference.com/w/cpp/language/using_declaration
If one of the inherited constructors of Base happens to have the signature that matches a copy/move constructor of the Derived, it does not prevent implicit generation of Derived copy/move constructor (which then hides the inherited version, similar to using operator=).
所以
struct C :public A { using A::A; };
是
struct C :public A
{
using A::A;
C(const C&) = default;
C(C&&) = default;
};
其中 C(const C&) = default;
类似于
C(const C& c) : A(static_cast<const A&>(c)) {}
所以
struct A {
template <typename... Args>
A (Args&&... args)
{ std::cout << "non-default ctor called\n"; }
A (A&) { std::cout << "copy ctor from non-const ref\n"; }
};
选择了模板构造函数,但是
struct A {
template <typename... Args>
A (Args&&... args)
{ std::cout << "non-default ctor called\n"; }
A (const A&) { std::cout << "copy ctor from const ref\n"; }
A (A&) { std::cout << "copy ctor from non-const ref\n"; }
};
A (const A&)
被选中。
如您所见,还有一个缺陷:
The semantics of inheriting constructors were retroactively changed by a defect report against C++11. Previously, an inheriting constructor declaration caused a set of synthesized constructor declarations to be injected into the derived class, which caused redundant argument copies/moves, had problematic interactions with some forms of SFINAE, and in some cases can be unimplementable on major ABIs. Older compilers may still implement the previous semantics.
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0136r1.html
有了那个缺陷,你的 class C 会是
struct C :public A
{
using A::A;
template <typename ...Ts>
C(Ts&&... ts) : A(std::forward<Ts>(ts)...) {} // Inherited.
C(const C&) = default;
C(C&&) = default;
};
所以你调用 C(C& c) : A(c) {}
(在模板替换之后)。