jjzjj

c++ - 自动生成的 move 构造函数导致非法行为

coder 2024-02-03 原文

我问a question about move constructors,但我尚未接受答案,因为即使在我开始对其他方面有所了解时,我对问题的某些方面也感到更加困惑。特别是,我发现了一个令人惊讶的情况,其中g++和clang++都生成不正确的move-constructor。
问题总结

  • g++和clang++显然违反了以下规则:明确定义析构函数时,不生成move-constructors。为什么?这是一个错误,还是我误会发生了什么?
  • 为了正确起见,这些(可能是非法的)move构造函数应使RHS指针成员无效,但它们不会无效。为什么不?
  • 看来,避免不良行为的唯一方法是为每个在其析构函数中使用delete的类显式定义一个正确的move构造函数。 Qt库(版本5.4)可以做到这一点吗?

  • 第1部分:非法自动生成的构造函数?
    考虑以下代码:
    class NoMove
    {
      public:
        ~NoMove() {}
    };
    int main()
    {
      std::cout << "NoMove move-constructible? " <<
        std::is_move_constructible<NoMove>::value << std::endl;
    }
    
    g++ 4.9.2和clang++ 3.5.1一起编译时,此代码将输出:
    NoMove move-constructible? 1
    
    ...但是由于NoMove具有明确定义的析构函数,所以我希望neither a move constructor nor a copy constructor should be auto-generated。注意,意外的构造函数生成不是由于析构函数是无关紧要的事实引起的;当析构函数delete[]为数组(!!)时,我得到相同的行为,甚至可以编译需要有效的move构造函数(!!!!!)的代码。 (请参见下面的示例。)这是怎么回事?在此处自动生成move构造函数合法吗?如果是这样,为什么?
    第2部分:(可能是非法的)自动生成的构造函数会导致未定义的行为?
    似乎在涉及delete时提供安全的move构造函数is fairly simple,但我只是想确保自己理解:当一个类包含指针成员并拥有基础数据时,在任何情况下它都不正确且不够用将目标指针设置为旧值后,move构造函数使RHS指针无效?
    考虑以下示例,该示例与上面的NoMove示例相似,并且基于我的original question:
    class DataType
    {
      public:
        DataType()
        {
          val = new int[35];
        }
        ~DataType()
        {
          delete[] val;
        }
      private:
        int* val;
    };
    
    class Marshaller
    {
      public:
        Marshaller()=default;
        DataType toDataType() &&
        {
          return std::move(data);
        }
      private:
        DataType data;
    };
    
    void DoMarshalling()
    {
      Marshaller marshaller;
      // ... do some marshalling...
      DataType marshalled_data{std::move(marshaller).toDataType()};
    }
    
    这样编译就可以了,这表明DataType具有自动生成的move构造函数。当然,在运行时,它会导致两次删除错误。
    现在,如果自动生成的move构造函数使RHS指针无效,那就可以了。因此,如果可以在此处自动生成move构造函数,那为什么不能安全地完成呢?使这项工作有效的move构造函数很简单:
    DataType(DataType&& rhs) :
      val{rhs.val}
    {
      rhs.val = nullptr;
    }
    
    (对吗?我有什么遗漏吗?应该是val{std::move(rhs.val)}吗?)
    自动生成似乎是一个非常安全的函数。编译器知道rhs是一个r值,因为函数原型(prototype)如此说,因此完全可以接受对其进行修改。因此,即使DataType的析构函数不是delete[] val,似乎也没有任何理由不使自动生成的版本中的rhs无效,但我认为这会导致微不足道的性能损失。
    因此,如果编译器正在自动生成此方法-再次,它不应该这样做,尤其是因为我们可以使用unique_ptr从标准库代码中轻松获得这种确切的行为-为什么它会错误地自动生成
    第3部分:在Qt中避免这种行为(尤其是Qt 5.4中的QByteArray)
    最后,一个(希望)很简单的问题:Qt 5.4的堆分配类(例如QByteArray(这是我最初在原始问题中实际使用的DataType))是否已正确实现了move构造函数,从而使所有从其拥有的移出都无效指针?
    我什至都不想问,因为Qt看起来很可靠,而且我还没有看到任何双重删除错误,但是鉴于这些不正确的编译器生成的move构造函数让我措手不及,因此我担心很容易以错误的方式构造完结的库,而该库在其他情况下却无法很好地实现。
    相关地,在C++11之前编写的没有显式move-constructor的Qt库又如何呢?如果我在这种情况下意外地强制了一个自动生成的move构造函数,那么有人知道吗,例如,使用符合C++ 11的编译器进行Qt 3编译会在这种用例中引起未定义的破坏行为吗?

    最佳答案

    问题是您混淆了is_move_constructible并“具有 move 构造函数”。 is_move_constructible<T>不会测试T是否具有move构造函数。它测试是否可以从T类型的右值构造T。并且const T&可以绑定(bind)到T右值。

    您所看到的是自动生成的拷贝构造函数T(const T&)正在进行其工作-惨遭失败。

    I would expect that neither a move constructor nor a copy constructor should be auto-generated.



    您的链接讨论了move构造函数。它没有讨论拷贝构造函数,如果不声明它,则总是隐式声明。

    现在,如果您声明了 move 操作,则隐式声明的拷贝构造函数将被定义为删除,但您并未这样做,因此将其定义为默认值并执行成员复制。 [class.copy]/p7:

    If the class definition does not explicitly declare a copy constructor, one is declared implicitly. If the class definition declares a move constructor or move assignment operator, the implicitly declared copy constructor is defined as deleted; otherwise, it is defined as defaulted (8.4). The latter case is deprecated if the class has a user-declared copy assignment operator or a user-declared destructor.

    关于c++ - 自动生成的 move 构造函数导致非法行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29614410/

    有关c++ - 自动生成的 move 构造函数导致非法行为的更多相关文章

    1. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

      我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

    2. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

      很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

    3. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

      我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

    4. ruby - 在 jRuby 中使用 'fork' 生成进程的替代方案? - 2

      在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',

    5. ruby - 如何使用 Ruby aws/s3 Gem 生成安全 URL 以从 s3 下载文件 - 2

      我正在编写一个小脚本来定位aws存储桶中的特定文件,并创建一个临时验证的url以发送给同事。(理想情况下,这将创建类似于在控制台上右键单击存储桶中的文件并复制链接地址的结果)。我研究过回形针,它似乎不符合这个标准,但我可能只是不知道它的全部功能。我尝试了以下方法:defauthenticated_url(file_name,bucket)AWS::S3::S3Object.url_for(file_name,bucket,:secure=>true,:expires=>20*60)end产生这种类型的结果:...-1.amazonaws.com/file_path/file.zip.A

    6. ruby - 在没有 sass 引擎的情况下使用 sass 颜色函数 - 2

      我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re

    7. ruby - RuntimeError(自动加载常量 Apps 多线程时检测到循环依赖 - 2

      我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("

    8. ruby-on-rails - 在 ruby​​ 中使用 gsub 函数替换单词 - 2

      我正在尝试用ruby​​中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了

    9. ruby - 在 Ruby 中有条件地定义函数 - 2

      我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

    10. ruby-on-rails - Ruby on Rails - 为文本区域和图片生成列 - 2

      我是Rails的新手,所以请原谅简单的问题。我正在为一家公司创建一个网站。那家公司想在网站上展示它的客户。我想让客户自己管理这个。我正在为“客户”生成一个表格,我想要的三列是:公司名称、公司描述和Logo。对于名称,我使用的是name:string但不确定如何在脚本/生成脚手架终端命令中最好地创建描述列(因为我打算将其设置为文本区域)和图片。我怀疑描述(我想成为一个文本区域)应该仍然是描述:字符串,然后以实际形式进行调整。不确定如何处理图片字段。那么……说来话长:我在脚手架命令中输入什么来生成描述和图片列? 最佳答案 对于“文本”数

    随机推荐