简体   繁体   English

当我不传递类的实例时,为什么调用复制构造函数?

[英]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. 为了保证复制省略,不需要访问复制/移动构造函数。

LIVE DEMO from GCC 来自GCC的现场演示

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 -> intint -> 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.

相关问题 为什么在将原子传递给可变参数时调用复制构造函数? - Why is a copy constructor being called when passing an atomic into a variadic argument? 为什么复制构造函数在通过const引用传递临时时被调用? - why copy constructor is called when passing temporary by const reference? 当我从类成员函数返回* this时,为什么要调用我的复制构造函数? - Why is my copy constructor being called when I return *this from a class member function? 为什么不调用成员类的复制构造函数? - Why isnt the copy constructor of member class called? 为什么没有调用此模板 class 的复制构造函数? - Why is the copy constructor of this template class not getting called? 当我添加一个不同的对象作为复制构造函数的参数时,为什么调用复制构造函数? - Why the copy constructor is called when I add a different object which is an argument in copy constructor? 当按值传递右值到函数时,为什么不调用复制构造函数 - Why isn't the copy-constructor called when passing rvalue by value to function 为什么复制构造函数被调用,即使我实际上是在C ++中复制到已经创建的对象? - why copy constructor is getting called even though I am actually copying to already created object in C++? 为什么不调用复制构造函数? - Why is the copy constructor not called? 为什么不调用复制构造函数? - Why the copy constructor is not called?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM