我有一个类Server,它有一个构造函数:
Server::Server(int port) {
// initialize some class variables
port_ = port;
//...
}
我尝试像这样创建一个类的实例:
int main(int argc, char** argv) {
int port = 3000;
Server server = Server(port);
}
我得到了这个编译错误:
server_main.cpp:32:32: error: use of deleted function ‘Server::Server(const Server&)’
Server server = Server(port);
^
现在,我理解为什么隐式删除了复制构造函数,但为什么要调用它?
如果我在课程中添加了一个复制构造函数,那么错误就会消失。还有其他方法可以避免这种情况吗?
答案 0 :(得分:4)
Server server = Server(port);
是copy initialization;您正在从临时server
初始化Server
。
copy elision可能会发生,但在C ++ 17之前无法保证。甚至可能不会调用copy- / move-构造函数,但仍然必须存在且可访问(好像根本没有发生优化),否则程序就会格式不正确。
您可以将其更改为direct initialization,直接调用Server::Server(int)
:
Server server(port);
或direct list initialization(自C ++ 11以来):
Server server{port};
修改强>
从C ++ 17开始,copy elision可以保证这种情况。
在下列情况下,编制者需要省略 类对象的复制和移动构造函数,即使复制/移动也是如此 构造函数和析构函数具有可观察到的副作用:
在初始化中,如果初始化表达式是prvalue而且是 cv-源类型的非限定版本与该类相同 目的地的类,初始化表达式用于 初始化目标对象:
T x = T(T(T())); // only one call to default constructor of T, to initialize x
所以你的代码适用于C ++ 17;对于保证复制省略,不需要复制/移动构造函数。
答案 1 :(得分:3)
从非常讨厌的迂腐观点来看,目前提供的许多答案(如果不是全部的话)都有些误导。
在C ++中,左侧和右侧具有相同类型的复制初始化以特殊方式处理:它立即被解释为等效的直接初始化。
来自[dcl.init] / 16:
- 如果目标类型是(可能是cv限定的)类类型:
- 如果初始化是直接初始化,或如果是 copy-initialization所在的cv-nonqualified版本的源码 type与类的类相同,或者是类的派生类 目的地,构造函数被认为是......
这意味着您的复制初始化
Server server = Server(port);
实际上是作为直接初始化
处理的Server server(Server(port));
并根据直接初始化规则进一步处理。
直接初始化规则说,重载决策用于选择构造函数,在这种情况下选择的构造函数是复制构造函数(在您的情况下删除,因此错误)。
所以,最终结果是一样的 - 复制构造函数是必需的。但是,需要它的标准逻辑的“分支”不是负责复制初始化的,而是负责直接初始化的那些。
在这种情况下,差异纯粹是概念性的。但是在C ++ 98时代,这种模糊的区别在[现在被遗忘] std::auto_ptr
指针(re:auto_ptr_ref
及其工作方式)的功能中发挥了重要作用。实际上,这通常被视为Move Constructor模式(https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Move_Constructor)的早期惯用实现。
一个简单的例子,说明特殊处理可能如下所示
struct A
{
A() {}
A(A&) {}
A(int) {}
operator int() const { return 42; }
};
struct B
{
B();
operator int() const { return 42; }
};
int main()
{
A a1 = A(); // OK
A a2 = B(); // Error
}
请注意,即使右侧的两个类都提供了用户定义的int
转换,但只有第一个初始化编译并使用A::A(int)
构造函数。第二个失败了。
第二次初始化按照通常的复制初始化规则进行。并且为了成功,它需要两个用户定义的转换(B -> int
和int -> A
),这是无法隐式完成的。
根据直接初始化规则处理第一次初始化,从而有效地使int -> A
转换显式化。这个初始化现在只需要一个隐式的用户定义转换(A -> int
),这很好。
答案 2 :(得分:2)
因为您复制初始化server
对象。
定义
Server server = Server(port);
相当于
Server server(Server(port));
您可能希望通过执行
显式使用构造函数Server server(port);
答案 3 :(得分:2)
复制初始化,=
中的Server server = Server{port};
语法要求复制构造函数或移动构造函数存在且可访问。
由于您的复制构造函数不存在,请尝试提供移动构造函数。
如果你不能,那么你唯一的办法是使用直接初始化语法,例如: Server server{port};