[英]Why is the copy constructor called when I am not passing an instance of the class?
I have a class Server which has a constructor: 我有一个具有构造函数的Server类:
Server::Server(int port) {
// initialize some class variables
port_ = port;
//...
}
I try to create an instance of the class like so: 我尝试创建此类的实例,如下所示:
int main(int argc, char** argv) {
int port = 3000;
Server server = Server(port);
}
And I get this compile error: 我得到这个编译错误:
server_main.cpp:32:32: error: use of deleted function ‘Server::Server(const Server&)’
Server server = Server(port);
^
Now, I understand why the copy constructor was implicitly deleted, but why is it being called? 现在,我了解了为什么隐式删除了副本构造函数,但是为什么要调用它呢?
The error goes away if I add a copy constructor to the class. 如果将复制构造函数添加到类中,该错误就会消失。 Is there any other way to avoid this?
还有其他方法可以避免这种情况吗?
Server server = Server(port);
is copy initialization ; 是复制初始化 ; You're initializing
server
from a temporary Server
. 您正在从临时
Server
初始化server
。
copy elision might take place, but not guaranteed until C++17. 复制省略可能会发生,但要等到C ++ 17才能保证。 Even copy-/move-constructor might not be called, but still must be present and accessible (as if no optimization happened at all), otherwise the program is ill-formed.
甚至copy / move-constructor可能不会被调用,但仍然必须存在并且可以访问(好像根本没有优化发生),否则程序格式错误。
You could change it to direct initialization , which will invoke Server::Server(int)
directly: 您可以将其更改为直接初始化 ,这将直接调用
Server::Server(int)
:
Server server(port);
Or direct list initialization (Since C++11): 或直接列表初始化 (自C ++ 11起):
Server server{port};
EDIT 编辑
Since C++17, copy elision is guaranteed for this case. 从C ++ 17开始,在这种情况下可以保证复制省略 。
Under the following circumstances, the compilers are required to omit the copy- and move- constructors of class objects even if copy/move constructor and the destructor have observable side-effects:
在以下情况下,即使复制/移动构造函数和析构函数具有明显的副作用,也要求编译器忽略类对象的复制和移动构造函数:
In initialization, if the initializer expression is a prvalue and the cv-unqualified version of the source type is the same class as the class of the destination, the initializer expression is used to initialize the destination object:
在初始化中,如果初始化器表达式是prvalue,并且源类型的cv不合格版本与目标类是同一类,则初始化器表达式用于初始化目标对象:
T x = T(T(T())); // only one call to default constructor of T, to initialize x
So your code will work well with C++17; 因此,您的代码将与C ++ 17配合使用; For copy elision being guaranteed, copy/move constructor is not required to be accessible.
为了保证复制省略,不需要访问复制/移动构造函数。
From the very annoyingly pedantic point of view, many of the currently provided answers (if not all of them) are slightly misleading. 从非常令人讨厌的书呆子角度来看,许多当前提供的答案(如果不是全部的话)会产生误导。
In C++ copy-initialization with the same type on the left-hand side and right-hand side is treated in a special way: it is immediately interpreted as an equivalent direct-initialization. 在C ++中,左侧和右侧具有相同类型的复制初始化以特殊方式处理:它立即被解释为等效的直接初始化。
From [dcl.init]/16: 从[dcl.init] / 16:
— If the destination type is a (possibly cv-qualified) class type:
—如果目标类型是(可能是cv限定的)类类型:
— If the initialization is direct-initialization, or if it is copy-initialization where the cv-unqualified version of the source type is the same class as, or a derived class of, the class of the destination , constructors are considered...
—如果初始化是直接初始化, 或者是复制初始化,其中源类型的cv不合格版本与destination的类相同,或者是其派生类 ,则考虑构造函数...
This means that your copy-initialization 这意味着您的副本初始化
Server server = Server(port);
is actually handled as direct-initialization 实际上被当作直接初始化
Server server(Server(port));
and is further processed in accordance with the rules of direct-initialization. 并根据直接初始化的规则进行进一步处理。
Rules of direct-initialization say that overload resolution is used to choose a constructor and the constructor chosen in this case is the copy-constructor (which is deleted in your case, hence the error). 直接初始化的规则说,重载解析用于选择一个构造函数,在这种情况下选择的构造函数是copy-constructor(在您的情况下被删除,因此是错误)。
So, the end result is the same - the copy constructor is required. 因此,最终结果是相同的-需要复制构造函数。 But "branches" of the standard logic that make it required are not the ones responsible for copy-initialization, but rather the ones responsible for direct-initialization.
但是,要求它的标准逻辑的“分支”不是负责复制初始化的,而是负责直接初始化的。
In this case the difference is purely conceptual. 在这种情况下,差异纯粹是概念上的。 But back in the days of C++98 this obscure distinction payed important role in the functionality of [now forgotten]
std::auto_ptr
pointer (re: auto_ptr_ref
and how it worked). 但是,在C ++ 98时代,这种模糊的区别在[现在已被遗忘的]
std::auto_ptr
指针(re: auto_ptr_ref
及其工作方式)的功能中发挥了重要作用。 This is actually often seen as an early idiomatic implementation of Move Constructor pattern ( https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Move_Constructor ). 实际上,这通常被视为Move Constructor模式的早期惯用实现( https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Move_Constructor )。
A simple example that illustrates that special handling might look as follows 一个简单的示例说明特殊处理可能如下所示
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
}
Note that even though both classes on the right-hand side provide a user-defined conversion to int
, only the first initialization compiles and uses A::A(int)
constructor. 请注意,即使右侧的两个类都提供了用户定义的
int
转换,也只有第一个初始化才编译并使用A::A(int)
构造函数。 The second one fails. 第二个失败。
The second initialization proceeds in accordance with usual copy-initialization rules. 第二次初始化按照通常的复制初始化规则进行。 And in order to succeed it needs two user-defined conversions (
B -> int
and int -> A
), which cannot be done implicitly. 为了成功,它需要两次用户定义的转换(
B -> int
和int -> A
),这些转换不能隐式完成。
The first initialization is treated in accordance with direct-initialization rules, thus effectively making the int -> A
conversion explicit. 根据直接初始化规则处理第一次初始化,从而有效地使
int -> A
转换成为显式。 This initialization now needs only one implicit user-defined conversion ( A -> int
), which is fine. 现在,此初始化仅需要一个隐式用户定义的转换(
A -> int
),这很好。
Because you copy-initialize the server
object. 因为您复制初始化
server
对象。
The definition 定义
Server server = Server(port);
is equivalent to 相当于
Server server(Server(port));
You might want to use the constructor explicitly by doing 您可能想通过以下方式显式使用构造函数
Server server(port);
Copy initialization , the =
syntax as in Server server = Server{port};
复制初始化 ,
=
语法如Server server = Server{port};
, requires that a copy constructor or a move constructor exists and is accessible. ,要求复制构造函数或move构造函数存在且可访问。
Since your copy constructor doesn't exist, try to provide a move constructor. 由于您的副本构造函数不存在,请尝试提供move构造函数。
If you can't, then your only recourse is to use direct initialization syntax, eg Server server{port};
如果不能,那么唯一的办法就是使用直接初始化语法,例如
Server server{port};
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.