为什么编译器在这个上烦我?

时间:2011-11-02 11:52:43

标签: c++

(使用Visual C ++ 2010,在调试中进行编译并关闭优化

我有以下非常简单的课程:

class exampleClass
{
public:
    exampleClass()
    {
        cout << "in the default ctor" << endl;
    }
private:
    exampleClass (const exampleClass& e)
    {
        cout << "in the copy ctor" << endl;
    }
};

当我尝试使用以下main编译它时:

#include <iostream>
using namespace std;

int main()
{
    exampleClass e1=exampleClass();
    return 0;
}

我收到编译错误:

'exampleClass::exampleClass' : cannot access private
                               member declared in class 'exampleClass'

当我从复制文件中删除访问修饰符“private”时,程序会编译并打印

in the default ctor

为什么会这样?如果编译器无论如何都不会调用copy ctor,为什么它会烦扰我?

由于有些人错过了第一行(至少在一些编辑之前),我将重复它:

我在调试中编译并关闭了优化。

9 个答案:

答案 0 :(得分:16)

这种类型的初始化称为复制初始化。我相信C ++ 11标准中的以下条款适用于此处(第8.5.16段,第204页):

  

如果初始化是直接初始化,或者是初始化   copy-initialization所在的cv-nonqualified版本的源码   type与类的类相同,或者是类的派生类   目的地,建设者被考虑。适用的构造函数   列举(13.3.1.3),并通过过载选择最好的一个   决议(13.3)。如此选择的构造函数被调用以初始化   对象,初始化表达式或表达式列表作为其对象   参数(一个或多个)。如果没有构造函数适用,或者重载解析是   暧昧,初始化是不正确的。

在这种情况下,最适用的构造函数是copy ctor,它是私有的,因此是错误消息。

为了进一步回答您的问题,当复制版本是私有的时,由于标准规定的规则,您的程序根本不允许通过编译器检查。当您公开复制ctor时,程序将变为有效,但对复制程序的调用将被优化掉。

编辑: 好的,详细说明上一段。你在这里处理所谓的copy elision。虽然在这种情况下可以进行复制省略,但标准要求您为班级提供可访问复制文件。

答案 1 :(得分:12)

exampleClass e1=exampleClass();

这将首先使用默认构造函数创建临时exampleClass,然后使用复制构造函数将其复制到e1。这将调用私有拷贝构造函数,从而给你错误。使用默认构造函数实例化类实例的正确方法是:

exampleClass e1;

答案 2 :(得分:4)

编译器需要在那里 bug 。虽然可以省略副本,但标准要求可以为该类型的构造访问复制构造函数。当然,您可以简化代码并完全避免复制构造:

exampleClass e1; // Will call exampleClass::exampleClass()

答案 3 :(得分:1)

exampleClass e1=exampleClass();

与:

相同
exampleClass e1(exampleClass());

即它调用(私有)复制构造函数。

答案 4 :(得分:1)

这是因为在编译时,编译器会检查用户尝试访问的函数是否真的可以访问。因此,当您使用exampleClass e1=exampleClass();时,它首先检查复制构造函数是否可访问。它会发出错误,因为复制构造函数不是私有的。 记住,在这一点上,编译器还没有进入优化阶段,在那里它做了一些聪明的事情,就是跳过复制构造函数。

当你将copy-constructor公之于众时,编译器成功地完成了解析代码的阶段,并确保一切都是可访问的并且是有序的(实际上还有更多),然后在优化阶段,通常在“释放”模式下,它会执行聪明的操作并绕过复制构造函数的使用。但是,如果您在“调试”模式下尝试相同的代码,您会看到复制构造函数被调用。

答案 5 :(得分:1)

每个人都解释了如何实例化一个对象,而@Grigory Javadyan在复制省略方面做得很好。看起来,即使在调试模式下,MSVC也会进行这种优化(所谓的返回值优化)。

exampleClass e1=exampleClass();

相同
exampleClass giveExample()
{
  return exampleClass();
}

exampleClass e1 = giveExample();

您将看到不会调用复制ctor。

但是在这里:

exampleClass giveExample()
{
  exampleClass example;
  return example;
}

exampleClass e1 = giveExample();

您将看到另一个输出行:

in the copy ctor

因为您强制编译器首先生成一个对象然后将其返回。

Hereherehere我可以找到一些与您类似的问题。

PS。链接#2来自另一个Q&amp; A站点。我希望这不是问题。

答案 6 :(得分:0)

因为复制构造函数是私有的..

您的代码是

  • 创建临时的exampleClass并调用默认构造函数exampleClass()
  • 尝试使用私有拷贝构造函数
  • 将生成的临时对象分配给e1

答案 7 :(得分:0)

这不是你如何在C ++中实例化一个对象。如果你想在堆栈上分配它,你写:

exampleClass e1;

并且你已经完成了,因为exampleClass'构造函数不接受任何参数。

否则,如果要在堆上分配它,请编写:

exampleClass e1 = new exampleClass(); 

您编写它的方式实际上创建了一个临时对象,并在该临时对象上调用复制构造函数来创建e1。问题是你的copy-ctor是私有的,所以编译器的错误信息。

答案 8 :(得分:0)

当你写

exampleClass e1 = exampleClass() 

与写作相同

exampleClass e1( exampleClass() );

调用副本ctor。