C ++ 11非静态成员初始化和删除拷贝构造函数 [英] C++11 non-static member initializers and deleted copy constructor

查看:144
本文介绍了C ++ 11非静态成员初始化和删除拷贝构造函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图编译以下简单的代码与GCC 4.7.2(MinGW)。这里我使用C ++ 11功能 - 非静态成员初始化器:

  #include< iostream> 
using namespace std;

struct A
{
int var;

A()
{
cout<< 一个;
}

A(int i)
{
cout< A(int i)\\\
;
var = i;
}

A(const A&)= delete;
};

struct B
{
A a = 7;
};

int main()
{
B b;
cout<< b.a.var =<瓦尔
return 0;
}

此代码不会因为删除的复制构造函数必要的。这里是错误:

  main.cpp:27:11:错误:使用删除的函数'A :: A ;)'
main.cpp:13:5:error:declared here
main.cpp:在构造函数'constexpr B :: B()':
main.cpp:25:8 :错误:使用删除的函数'A :: A(const A&)'
main.cpp:13:5:错误:此处声明

如果我实现这样的复制构造函数:

  ; a)
{
cout<< A(const A&)\\\
;
var = a.var;
}

然后代码编译正确,程序给出预期输出:

  A(int i)
bavar = 7

因此,这意味着不使用复制构造函数,但为什么我不能删除它?



感谢您的回答。如果我使用 = ,标准需要复制或移动构造函数。要解决这个问题,我需要实现移动构造函数或使用直接初始化语法 A a {7}

解决方案

根据C ++ 11标准的第12.2 / 14节:




T x = a;

形式进行初始化 p>以及在参数传递,函数返回,抛出异常(15.1),处理异常(15.3)和聚合成员初始化(8.5.1)被称为复制初始化。 [注意:复制初始化可以调用移动(12.8)。 -end note ]


复制初始化不能编译的原因是, strong>临时对象需要被创建(至少在逻辑上),并且被初始化的对象应该从中构造。



现在所有以前的答案只专注于复制构造函数,但这里的第一个问题是缺少一个移动构造函数



唉,删除复制构造函数会阻止隐式移动构造函数的生成。显式添加一个将修复问题:

  struct A 
{
int var;

A()
{
cout<< 一个;
}

A(int i)
{
cout< A(int i)\\\
;
var = i;
}

A(const A&)= delete;

//这使它工作
A(A& a)
{
cout< A(A&)\\\
`;
var = a.var;
}
};
移动构造函数是首选,因为临时创建的复制 -



当移动构造函数不存在时,编译器可以调用复制构造函数来执行初始化,因为常量左值引用可以绑定到右值引用和副本被视为未优化的移动。



但是,即使允许编译器允许调用移动或副本构造函数,操作的语义仍然必须检查。根据C ++ 11标准的第12.8 / 32节:


当满足复制操作的删除标准时,或者除了源对象是函数参数,并且要复制的对象由lvalue指定的事实之外,满足复制操作的标准时,重载分辨率选择构造函数首先执行复制,就像对象由右值指定。如果重载分辨率失败,或者如果所选构造函数的第一个参数的类型不是对象类型的可值引用(可能是cv限定的),则会再次执行重载分辨率,将对象视为左值。 [注意:无论是否发生复制检测,都必须执行此两级重载分辨率。 - end note] [...]


如果没有执行elision,它将确定要调用的构造函数。 blockquote>

因此,如果移动构造函数和复制构造函数都不存在,编译器会发出错误。



但是您可以直接初始化对象,而不是复制初始化它。只需使用直接初始化语法:

  struct B 
{
A a {7}
};

这将使移动构造函数和复制构造函数不必要,初始化对象。


I'm trying to compile the following simple code with GCC 4.7.2 (MinGW). Here I'm using C++11 feature - non-static member initializers:

#include <iostream>
using namespace std;

struct A
{
    int var;

    A()
    {
        cout << "A()\n";
    }

    A(int i)
    {
        cout << "A(int i)\n";
        var = i;
    }

    A(const A&) = delete;
};

struct B
{
    A a = 7;
};

int main()
{
    B b;
    cout << "b.a.var = " << b.a.var;
    return 0;
}

This code doesn't compile because of deleted copy-constructor which isn't necessary here. Here are errors:

main.cpp:27:11: error: use of deleted function 'A::A(const A&)'
main.cpp:13:5: error: declared here
main.cpp: In constructor 'constexpr B::B()':
main.cpp:25:8: error: use of deleted function 'A::A(const A&)'
main.cpp:13:5: error: declared here

If I implement copy-constructor like this:

A(const A& a)
{
    cout << "A(const A&)\n";
    var = a.var;
}

Then code compiles fine and program gives me expected output:

A(int i)
b.a.var = 7

So it means that copy constructor is not used, but why I can't delete it?

Edit: Thanks for your answers. Copy or move constructor is required by standard if I'm using =. To fix this problem I need to implement move constructor or use direct initialization syntax A a{7}.

解决方案

Per Paragraph 12.2/14 of the C++11 Standard:

The initialization that occurs in the form

T x = a;

as well as in argument passing, function return, throwing an exception (15.1), handling an exception (15.3), and aggregate member initialization (8.5.1) is called copy-initialization. [ Note: Copy-initialization may invoke a move (12.8). —end note ]

The reason why your copy-initialization doesn't compile is that during copy-initialization a temporary object needs to be created (at least logically), and the object being initialized shall be constructed from it.

Now all the previous answers seem to focus just on copy-constructors, but the first problem here is the absence of a move-constructor. As long as you provide one, then it is true that the copy constructor is not necessary.

Alas, deleting the copy constructor prevents the generation of an implicit move constructor. Adding one explicitly would fix the problem:

struct A
{
    int var;

    A()
    {
        cout << "A()\n";
    }

    A(int i)
    {
        cout << "A(int i)\n";
        var = i;
    }

    A(const A&) = delete;

    // THIS MAKES IT WORK
    A(A&& a)
    {
        cout << "A(A&&)\n`;
        var = a.var;
    }
};

Notice that when both the move-constructor and the copy-constructor are present, the move-constructor is preferred, because the temporary created for copy-initializing your object is an rvalue.

When a move-constructor is absent, the compiler can invoke the copy constructor to perform the initialization, because constant lvalue references can bind to rvalue references and copy is seen as an unoptimized move.

However, even though the compiler is allowed to elide the call to the move or the copy constructor, the semantics of the operation must still be checked. Per Paragraph 12.8/32 of the C++11 Standard:

When the criteria for elision of a copy operation are met or would be met save for the fact that the source object is a function parameter, and the object to be copied is designated by an lvalue, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue. If overload resolution fails, or if the type of the first parameter of the selected constructor is not an rvalue reference to the object’s type (possibly cv-qualified), overload resolution is performed again, considering the object as an lvalue. [ Note: This two-stage overload resolution must be performed regardless of whether copy elision will occur. It determines the constructor to be called if elision is not performed, and the selected constructor must be accessible even if the call is elided. —end note ] [...]

Therefore, an error is issued by the compiler if neither the move constructor nor the copy constructor are present.

If you want, however, you can direct-initialize your object rather than copy-initializing it. Just use the direct-initialization syntax instead:

struct B
{
    A a{7};
};

This will make the move-constructor and copy-constructor unnecessary, because no temporary is created when direct-initializing an object.

这篇关于C ++ 11非静态成员初始化和删除拷贝构造函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆