【问题标题】:Why is the compiler bugging me on this one?为什么编译器在这个问题上打扰我?
【发布时间】:2011-11-02 11:52:43
【问题描述】:

(使用 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'

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

in the default ctor

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

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

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

【问题讨论】:

  • 我怀疑编译阶段确实使用了复制构造函数,但优化阶段(在编译阶段之后)将其删除。所以这个错误是有道理的。好吧,从语义上讲,无论如何-您确实在那里使用了副本。但这只是我的猜测,我不知道编译器是如何工作的。附带说明 - 你为什么不改用exampleClass e1(); 呢?它也更短。
  • 复制省略是一种可能的优化,但不是必须的;生成的可执行文件必须以与没有优化的可执行文件相同的方式工作。
  • Vlix,我关闭了所有的优化(正如我开头提到的,我在调试中编译)。关于你的旁注——我只是想更好地理解 c++ 程序的编译,结果出现了这个(减少的)错误。
  • “必须以同样的方式工作”——我也这么认为,但对于 RVO (en.wikipedia.org/wiki/Return_value_optimization) 来说不是这样

标签: c++


【解决方案1】:

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

如果初始化是直接初始化,或者如果是 复制初始化,其中源的 cv 不合格版本 type 与该类的类相同或派生类 目的地,构造函数被考虑。适用的构造函数 枚举(13.3.1.3),通过重载选择最好的 决议 (13.3)。调用如此选择的构造函数进行初始化 对象,以初始化表达式或表达式列表作为其 论点。如果没有构造函数适用,或者重载决议是 模棱两可,初始化格式不正确。

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

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

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

【讨论】:

    【解决方案2】:
    exampleClass e1=exampleClass();
    

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

    exampleClass e1;
    

    【讨论】:

    • 仔细阅读问题:在 OPs 情况下不调用复制构造函数。 (我想它已经优化了)。严格来说,这并不能回答问题。
    • 优化前会抛出错误。它确实在逻辑上调用了复制构造函数。
    • -1。 exampleClass e1(); 声明了一个名为 e1 的函数,不带任何参数并返回 exampleClass。使用默认构造函数构造对象的正确方法是:exampleClass e1; 虽然其余的答案都是正确的,所以我取消了我的 -1。
    • @Benoit:糟糕,让我来解决这个问题。
    • 严格来说,它回答了这个问题。在给出的示例中,您可以通过简单地编写 exampleClass e1; 来声明 e1。复制构造函数被声明为私有,其中使用赋值运算符定义不可复制的类。有关不可复制类概念的说明,请参阅 Non-copyable Mixin 文章。
    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:
      exampleClass e1=exampleClass();
      

      等同于:

      exampleClass e1(exampleClass());
      

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

      【讨论】:

        【解决方案5】:

        这是因为在编译时,编译器会检查用户试图访问的函数是否真的可以访问。因此,当您使用exampleClass e1=exampleClass(); 时,它首先检查复制构造函数是否可访问。它吐出一个错误,因为复制构造函数不是私有的。 请记住,此时编译器还没有进入优化阶段,它会巧妙地跳过复制构造函数。

        当您公开复制构造函数时,编译器会成功通过解析代码并确保所有内容都可访问且井井有条(实际上不止如此),然后在优化阶段,通常在“发布”模式下打开它会做一些聪明的事情并绕过复制构造函数的使用。但是,如果您在“调试”模式下尝试相同的代码,您会看到复制构造函数确实被调用了。

        【讨论】:

        • @downvoter:想解释一下为什么投反对票?如果可以的话,我也会学到一些东西,而不是仅仅为了它而投反对票!
        【解决方案6】:

        每个人都解释了你应该如何实例化一个对象,@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。 Link#2 来自另一个问答网站。我希望这不是问题。

        【讨论】:

          【解决方案7】:

          这不是在 C++ 中实例化对象的方式。如果你希望它在堆栈上分配,你写:

          exampleClass e1;
          

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

          否则,如果你想在堆上分配它,你写:

          exampleClass e1 = new exampleClass(); 
          

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

          【讨论】:

            【解决方案8】:

            当你写作时

            exampleClass e1 = exampleClass() 
            

            和写作一样

            exampleClass e1( exampleClass() );
            

            调用复制ctor。

            【讨论】:

              【解决方案9】:

              因为拷贝构造函数是私有的..

              你的代码是

              • 创建一个临时示例类并调用默认构造函数exampleClass()
              • 尝试使用私有复制构造函数将生成的临时对象分配给 e1

              【讨论】:

              • @Andres K.,StevieG- 未调用复制 ctor:查看输出。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2012-05-23
              相关资源
              最近更新 更多