删除复制构造函数会破坏继承的构造函数
Deleting copy constructor breaks inherited constructors
我正在尝试使用 C++11 的构造函数继承功能。以下片段(从某处复制,我不记得从哪里复制了)完全可以正常工作:
#include <iostream>
struct Base {
Base() : Base(0) {}
Base(int a) : Base(a, 0) {}
Base(int a, double b) { std::cout << "Base(" << a << "," << b << ")" << std::endl; }
};
struct Derived : Base {
using Base::Base;
Derived(const Derived& that) = delete; // This line is the culprit
};
int main(int argc, char* argv[]) {
Derived d1;
Derived d2(42);
Derived d3(42, 3.14);
}
也就是直到添加注释标记的行;因为那时一切都乱套了:
> g++ -std=c++11 -o test test.cpp
test.cpp: In function ‘int main(int, char**)’:
test.cpp:18:11: error: no matching function for call to ‘Derived::Derived()’
Derived d1;
^
test.cpp:18:11: note: candidates are:
test.cpp:13:16: note: Derived::Derived(int)
using Base::Base;
^
test.cpp:13:16: note: candidate expects 1 argument, 0 provided
test.cpp:13:16: note: Derived::Derived(int, double)
test.cpp:13:16: note: candidate expects 2 arguments, 0 provided
似乎删除复制构造函数也会以某种方式使 Base
中的默认构造函数无法访问。谷歌搜索问题并没有带来任何有用的信息;所以建议 this issue,但据我所知,我没有在此代码段中使用复制初始化。有人可以阐明这里发生的事情吗?
(生成上述消息的编译器是 GCC 4.8.2;但是,clang returns 类似的错误消息。)
问题在于,用 delete
标记复制构造函数会使它成为 用户声明的 ,这实际上删除了那个 class 的默认构造函数(在你的情况下 Derived
)。可以在这个简单的代码中看到行为:
struct X
{
X(const X&) = delete; // now the default constructor is not defined anymore
};
int main()
{
X x; // cannot construct X, default constructor is inaccessible
}
顺便说一句:即使 Base::Base()
会被继承,编译器也会将其视为
Derived(): Base(){}
。但是Derived
被删除了,所以不能真正调用Base::Base()
。通常,using Base::Base
语句只是编译器生成的相应 Derived(params): Base(params){}
.
的语法糖
无论何时定义自定义构造函数,都需要显式提供默认构造函数。即
Derived::Derived() = default;
继承构造函数不会获得特殊构造函数 -- 清空、复制、移动。这是因为你所要求的几乎总是一个坏主意。
检查:
struct base {
std::vector<int> data;
base(base const&)=default;
base(base&&)=default;
base(size_t n):data(n) {}
base()=default;
};
struct derived:base {
using base::base;
std::vector<char> more_data;
};
你真的想要derived(base const&)
存在吗?还是base(base&&)
?两者都会绝望地切片 derived
.
发生这些操作的危险 "accidentally" 意味着如果需要,您必须明确地引入它们。
默认情况下,copy/move/default ctors 恰好调用了 parent 版本,加上成员变量的 ctors。没有必要(通常)涉及从您的 parent.
继承它们
但是,一旦您 =delete
、=default
或定义了其中一个特殊构造函数,编译器将停止生成其他构造函数。所以你必须=default
其他的,如果你还想让他们留下来的话。
我正在尝试使用 C++11 的构造函数继承功能。以下片段(从某处复制,我不记得从哪里复制了)完全可以正常工作:
#include <iostream>
struct Base {
Base() : Base(0) {}
Base(int a) : Base(a, 0) {}
Base(int a, double b) { std::cout << "Base(" << a << "," << b << ")" << std::endl; }
};
struct Derived : Base {
using Base::Base;
Derived(const Derived& that) = delete; // This line is the culprit
};
int main(int argc, char* argv[]) {
Derived d1;
Derived d2(42);
Derived d3(42, 3.14);
}
也就是直到添加注释标记的行;因为那时一切都乱套了:
> g++ -std=c++11 -o test test.cpp
test.cpp: In function ‘int main(int, char**)’:
test.cpp:18:11: error: no matching function for call to ‘Derived::Derived()’
Derived d1;
^
test.cpp:18:11: note: candidates are:
test.cpp:13:16: note: Derived::Derived(int)
using Base::Base;
^
test.cpp:13:16: note: candidate expects 1 argument, 0 provided
test.cpp:13:16: note: Derived::Derived(int, double)
test.cpp:13:16: note: candidate expects 2 arguments, 0 provided
似乎删除复制构造函数也会以某种方式使 Base
中的默认构造函数无法访问。谷歌搜索问题并没有带来任何有用的信息;所以建议 this issue,但据我所知,我没有在此代码段中使用复制初始化。有人可以阐明这里发生的事情吗?
(生成上述消息的编译器是 GCC 4.8.2;但是,clang returns 类似的错误消息。)
问题在于,用 delete
标记复制构造函数会使它成为 用户声明的 ,这实际上删除了那个 class 的默认构造函数(在你的情况下 Derived
)。可以在这个简单的代码中看到行为:
struct X
{
X(const X&) = delete; // now the default constructor is not defined anymore
};
int main()
{
X x; // cannot construct X, default constructor is inaccessible
}
顺便说一句:即使 Base::Base()
会被继承,编译器也会将其视为
Derived(): Base(){}
。但是Derived
被删除了,所以不能真正调用Base::Base()
。通常,using Base::Base
语句只是编译器生成的相应 Derived(params): Base(params){}
.
无论何时定义自定义构造函数,都需要显式提供默认构造函数。即
Derived::Derived() = default;
继承构造函数不会获得特殊构造函数 -- 清空、复制、移动。这是因为你所要求的几乎总是一个坏主意。
检查:
struct base {
std::vector<int> data;
base(base const&)=default;
base(base&&)=default;
base(size_t n):data(n) {}
base()=default;
};
struct derived:base {
using base::base;
std::vector<char> more_data;
};
你真的想要derived(base const&)
存在吗?还是base(base&&)
?两者都会绝望地切片 derived
.
发生这些操作的危险 "accidentally" 意味着如果需要,您必须明确地引入它们。
默认情况下,copy/move/default ctors 恰好调用了 parent 版本,加上成员变量的 ctors。没有必要(通常)涉及从您的 parent.
继承它们但是,一旦您 =delete
、=default
或定义了其中一个特殊构造函数,编译器将停止生成其他构造函数。所以你必须=default
其他的,如果你还想让他们留下来的话。