草庐IT

c++ - 复制初始化和直接初始化有区别吗?

coder 2023-04-24 原文

假设我有这个功能:

void my_test()
{
    A a1 = A_factory_func();
    A a2(A_factory_func());

    double b1 = 0.5;
    double b2(0.5);

    A c1;
    A c2 = A();
    A c3(A());
}

在每个分组中,这些陈述是否相同?或者在某些初始化中是否有额外的(可能可优化的)拷贝?

我见过人们说这两件事。请引用 文字为证。还请添加其他案例。

最佳答案

C++17 更新

在C++17中,A_factory_func()的含义从创建临时对象 (C++<=14) 更改为仅指定此表达式在="" c++17="" 中初始化为(粗略地说)的任何对象的初始化。这些对象(称为“结果对象”)是由声明创建的变量(如="">a1 )、初始化结束时创建的人工对象或引用绑定(bind)需要一个对象(如 A_factory_func(); 中) . 在最后一种情况下,一个对象是人为创建的,称为“临时物化”,因为 A_factory_func() 没有变量或引用,否则需要对象存在)。

作为我们的例子,在 a1 的情况下和 a2特殊规则说,在这样的声明中,与 a1 类型相同的纯右值初始值设定项的结果对象。是可变的 a1 ,因此 A_factory_func()直接初始化对象a1 .任何中间函数式类型转换都不会产生任何效果,因为 A_factory_func(another-prvalue)只是“通过”外部纯右值的结果对象也是内部纯右值的结果对象。

A a1 = A_factory_func();
A a2(A_factory_func());

取决于什么类型A_factory_func()返回。我假设它返回一个 A - 然后它做同样的事情 - 除了当复制构造函数是显式时,第一个将失败。阅读 8.6/14
double b1 = 0.5;
double b2(0.5);

这样做也是一样的,因为它是一个内置类型(这意味着这里不是类类型)。阅读 8.6/14 .
A c1;
A c2 = A();
A c3(A());

这不是在做同样的事情。如果 A,则第一个默认值初始化是非 POD,不会对 POD 进行任何初始化(阅读 8.6/9)。第二个拷贝初始化:值初始化一个临时值,然后将该值复制到 c2 (阅读 5.2.3/28.6/14)。这当然需要一个非显式复制构造函数(阅读 8.6/1412.3.1/313.3.1.3/1)。第三个为函数 c3 创建函数声明返回 A这需要一个函数指针,指向一个返回 A 的函数。 (阅读 8.2)。

深入研究初始化 直接和复制初始化

虽然它们看起来相同并且应该做相同的事情,但这两种形式在某些情况下却截然不同。初始化的两种形式是直接初始化和复制初始化:
T t(x);
T t = x;

我们可以将行为归因于它们中的每一个:
  • 直接初始化的行为就像对重载函数的函数调用:在这种情况下,函数是 T 的构造函数。 (包括 explicit 个),参数为 x .重载解析将找到最匹配的构造函数,并在需要时进行任何所需的隐式转换。
  • 复制初始化构造了一个隐式转换序列:它试图转换 xT 类型的对象. (然后它可能会将该对象复制到要初始化的对象中,因此也需要一个复制构造函数 - 但这在下面并不重要)

  • 如您所见,复制初始化在某种程度上是关于可能的隐式转换的直接初始化的一部分:虽然直接初始化具有可调用的所有构造函数,此外还可以进行任何需要匹配参数类型的隐式转换,复制初始化可以只设置一个隐式转换序列。

    我努力了 got the following code to output different text for each of those forms , 不使用“明显”通过 explicit构造器。
    #include <iostream>
    struct B;
    struct A { 
      operator B();
    };
    
    struct B { 
      B() { }
      B(A const&) { std::cout << "<direct> "; }
    };
    
    A::operator B() { std::cout << "<copy> "; return B(); }
    
    int main() { 
      A a;
      B b1(a);  // 1)
      B b2 = a; // 2)
    }
    // output: <direct> <copy>
    

    它是如何工作的,为什么会输出该结果?
  • 直接初始化

    它首先对转换一无所知。它只会尝试调用构造函数。在这种情况下,以下构造函数可用并且完全匹配:
    B(A const&)
    

    调用该构造函数不需要任何转换,更不用说用户定义的转换(请注意,这里也没有发生 const 限定转换)。所以直接初始化会调用它。
  • 复制初始化

    如上所述,复制初始化会在a时构造一个转换序列。没有输入B或从它派生出来(这里显然就是这种情况)。所以它会寻找进行转换的方法,并会找到以下候选者
    B(A const&)
    operator B(A&);
    

    注意我是如何重写转换函数的:参数类型反射(reflect)了 this 的类型。指针,在非常量成员函数中指向非常量。现在,我们用 x 来称呼这些候选人。作为论据。赢家是转换函数:因为如果我们有两个候选函数都接受对同一类型的引用,那么较少的 const 版本获胜(顺便说一句,这也是喜欢非常量成员函数调用非-const 对象)。

    请注意,如果我们将转换函数更改为 const 成员函数,那么转换是不明确的(因为两者都有 A const& 的参数类型):Comeau 编译器正确拒绝它,但 GCC 在非迂腐模式下接受它.切换到 -pedantic不过,它也会输出适当的歧义警告。

  • 我希望这有助于更清楚地说明这两种形式的不同之处!

    关于c++ - 复制初始化和直接初始化有区别吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1051379/

    有关c++ - 复制初始化和直接初始化有区别吗?的更多相关文章

    1. ruby-on-rails - 未初始化的常量 Psych::Syck (NameError) - 2

      在我的gem中,我需要yaml并且在我的本地计算机上运行良好。但是在将我的gem推送到ruby​​gems.org之后,当我尝试使用我的gem时,我收到一条错误消息=>"uninitializedconstantPsych::Syck(NameError)"谁能帮我解决这个问题?附言RubyVersion=>ruby1.9.2,GemVersion=>1.6.2,Bundlerversion=>1.0.15 最佳答案 经过几个小时的研究,我发现=>“YAML使用未维护的Syck库,而Psych使用现代的LibYAML”因此,为了解决

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

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

    3. ruby-on-rails - 未在 Ruby 中初始化的对象 - 2

      我在Rails工作并有以下类(class):classPlayer当我运行时bundleexecrailsconsole然后尝试:a=Player.new("me",5.0,"UCLA")我回来了:=>#我不知道为什么Player对象不会在这里初始化。关于可能导致此问题的操作/解释的任何建议?谢谢,马里奥格 最佳答案 havenoideawhythePlayerobjectwouldn'tbeinitializedhere它没有初始化很简单,因为你还没有初始化它!您已经覆盖了ActiveRecord::Base初始化方法,但您没有调

    4. ruby - 触发器 ruby​​ 中 3 点范围运算符和 2 点范围运算符的区别 - 2

      请帮助我理解范围运算符...和..之间的区别,作为Ruby中使用的“触发器”。这是PragmaticProgrammersguidetoRuby中的一个示例:a=(11..20).collect{|i|(i%4==0)..(i%3==0)?i:nil}返回:[nil,12,nil,nil,nil,16,17,18,nil,20]还有:a=(11..20).collect{|i|(i%4==0)...(i%3==0)?i:nil}返回:[nil,12,13,14,15,16,17,18,nil,20] 最佳答案 触发器(又名f/f)是

    5. ruby-on-rails - ActionController::RoutingError: 未初始化常量 Api::V1::ApiController - 2

      我有用于控制用户任务的Rails5API项目,我有以下错误,但并非总是针对相同的Controller和路由。ActionController::RoutingError:uninitializedconstantApi::V1::ApiController我向您描述了一些我的项目,以更详细地解释错误。应用结构路线scopemodule:'api'donamespace:v1do#=>Loginroutesscopemodule:'login'domatch'login',to:'sessions#login',as:'login',via::postend#=>Teamroutessc

    6. ruby-on-rails - `a ||= b` 和 `a = b if a.nil 之间的区别? - 2

      我正在检查一个Rails项目。在ERubyHTML模板页面上,我看到了这样几行:我不明白为什么不这样写:在这种情况下,||=和ifnil?有什么区别? 最佳答案 在这种特殊情况下没有区别,但可能是出于习惯。每当我看到nil?被使用时,它几乎总是使用不当。在Ruby中,很少有东西在逻辑上是假的,只有文字false和nil是。这意味着像if(!x.nil?)这样的代码几乎总是更好地表示为if(x)除非期望x可能是文字false。我会将其切换为||=false,因为它具有相同的结果,但这在很大程度上取决于偏好。唯一的缺点是赋值会在每次运行

    7. ruby - 这两个 Ruby 类初始化定义有什么区别? - 2

      我正在阅读一本关于Ruby的书,作者在编写类初始化定义时使用的形式与他在本书前几节中使用的形式略有不同。它看起来像这样:classTicketattr_accessor:venue,:datedefinitialize(venue,date)self.venue=venueself.date=dateendend在本书的前几节中,它的定义如下:classTicketattr_accessor:venue,:datedefinitialize(venue,date)@venue=venue@date=dateendend在第一个示例中使用setter方法与在第二个示例中使用实例变量之间是

    8. ruby - 使用 `+=` 和 `send` 方法 - 2

      如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

    9. spring.profiles.active和spring.profiles.include的使用及区别说明 - 2

      转自:spring.profiles.active和spring.profiles.include的使用及区别说明下文笔者讲述spring.profiles.active和spring.profiles.include的区别简介说明,如下所示我们都知道,在日常开发中,开发|测试|生产环境都拥有不同的配置信息如:jdbc地址、ip、端口等此时为了避免每次都修改全部信息,我们则可以采用以上的属性处理此类异常spring.profiles.active属性例:配置文件,可使用以下方式定义application-${profile}.properties开发环境配置文件:application-dev

    10. ruby - 这两段代码有什么区别? - 2

      打印1:defsum(i)i=i+[2]end$x=[1]sum($x)print$x打印12:defsum(i)i.push(2)end$x=[1]sum($x)print$x后者是修改全局变量$x。为什么它在第二个例子中被修改而不是在第一个例子中?类Array的任何方法(不仅是push)都会发生这种情况吗? 最佳答案 变量范围在这里无关紧要。在第一段代码中,您仅使用赋值运算符=为变量i赋值,而在第二段代码中,您正在修改$x(也称为i)使用破坏性方法push。赋值从不修改任何对象。它只是提供一个名称来引用一个对象。方法要么是破坏性

    随机推荐