草庐IT

c++ - 何时为此结构调用复制构造函数?

coder 2023-06-03 原文

我正在尝试使用 {}-lists 进行一些测试。当我在 VS2015 中编译这个时,输出是

copy A 0

只是不明白,复制构造函数在哪里调用?

#include <iostream>

struct A
{
    A() = default;
    A(int i) : m_i(i) {}
    A(const A& a)
    {
        std::cout << "copy A " << m_i << std::endl;
    }
    int m_i = 0;
};

struct B
{
    B(const A& a) : m_a(a) {}
    B(const A& a1, const A& a2) {}
    A m_a;
};

int main()
{
    A a1{1}, a2{2};
    B b({ a1, a2 });
    return 0;
}

最佳答案

短版:

在像 B b({a1, a2}) 这样的直接初始化中,braced-init-list {a1, a2} 被视为 B 的构造函数的 一个 参数。此参数 {a1, a2} 将用于初始化构造函数的第一个参数。 B 包含一个隐式声明的构造函数 B(B const&)。一个引用 B const& 可以通过创建一个 临时 B{a1, a2} 初始化。这个临时包含一个 A 子对象,这个子对象最终会通过 B(B const&) 复制构造函数复制到 b.m_a

比较:

void foo(B const& b0);
foo({a1, a2}); // one argument, creates a temporary `B`

对于 B b{a1, a2}B b(a1, a2)B b 形式的初始化,我们不会看到任何复制= {a1, a2},因为这些情况将 a1a2 视为(单独的)参数 - 除非可行的 std::initializer_list 构造函数存在。


长版:

B 类包含以下构造函数:

B(const A& a) : m_a(a) {}                      // #0
B(const A& a1, const A& a2) {}                 // #1
B(B const&) = default; // implicitly declared     #2
B(B&&) = default;      // implicitly declared     #3

#3 将不会出现在 VS2013 中,因为缺乏对隐式提供的特殊移动功能的支持。 #0 没有在 OP 的程序中使用。

初始化B b({a1, a2}) 必须选择其中一个构造函数。我们只提供一个参数{a1, a2},所以#1 是不可行的。 #0 也不可行,因为 A 不能由两个参数构成。 #2 和 #3 仍然可行(#3 在 VS2013 中不存在)。

现在,重载解析尝试从 {a1, a2} 初始化 B const&B&&。将创建一个临时 B 并将其绑定(bind)到此引用。如果 #3 存在,重载解析会优先选择 #3 而不是 #2。

临时的创建再次查看上面显示的四个构造函数,但现在我们有两个参数a1a2(或initializer_list,但这在这里无关紧要)。 #1 是唯一可行的重载,临时是通过 B(const A& a1, const A& a2) 创建的。

所以,我们基本上以 B b( B{a1, a2} ) 告终。从临时 B{a1, a2}b 的复制(或移动)可以被省略(复制省略)。这就是为什么 g++ 和 clang++ 不调用 BA 的复制 ctor 或移动 ctor。

VS2013 似乎没有在这里省略复制构造,并且它不能移动构造,因为它不能隐式提供#3(VS2015 将解决这个问题)。因此,VS2013调用B(B const&),将B{a1, a2}.m_a复制到b.m_a。这会调用 A 的复制构造函数。

如果#3 存在,并且没有省略移动,则调用隐式声明的移动构造函数#3。由于 A 具有显式声明的复制构造函数,因此不会为 A 隐式声明移动构造函数。这也导致了从 B{a1, a2}.m_ab.m_a 的复制构造,但通过 B 的移动 ctor。


在 VS2013 中,如果我们手动将移动 ctor 添加到 AB,我们会注意到 A 将被移动复制的:

#include <iostream>
#include <utility>

struct A
{
    A() = default;
    A(int i) : m_i(i) {}
    A(const A& a)
    {
        std::cout << "copy A " << m_i << std::endl;
    }
    A(A&& a)
    {
        std::cout << "move A " << m_i << std::endl;
    }
    int m_i = 0;
};

struct B
{
    //B(const A& a) : m_a(a) {}
    B(const A& a1, const A& a2) {}
    B(B const&) = default;
    B(B&& b) : m_a(std::move(b.m_a)) {}
    A m_a;
};

通过跟踪 every 构造函数通常更容易理解此类程序。使用 MSVC 特有的 __FUNCSIG__(g++/clang++ 可以使用 __PRETTY_FUNCTION__):

#include <iostream>

#define PRINT_FUNCSIG() { std::cout << __FUNCSIG__ << "\n"; }

struct A
{
    A() PRINT_FUNCSIG()
    A(int i) : m_i(i) PRINT_FUNCSIG()
    A(const A& a) : m_i(a.m_i) PRINT_FUNCSIG()
    int m_i = 0;
};

struct B
{
    B(const A& a1, const A& a2) PRINT_FUNCSIG()
    B(B const& b) : m_a(b.m_a) PRINT_FUNCSIG()
    A m_a;
};

int main()
{
    A a1{1}, a2{2};
    B b({ a1, a2 });
    return 0;
}

此打印(不含评论):

__thiscall A::A(int)  // a1{1}
__thiscall A::A(int)  // a2{2}
__thiscall A::A(void) // B{a1, a2}.m_a, default-constructed
__thiscall B::B(const struct A &,const struct A &) // B{a1, a2}
__thiscall A::A(const struct A &) // b.m_a(B{a1, a2}.m_a)
__thiscall B::B(const struct B &) // b(B{a1, a2})

其他事实:

  • VS2015 和 VS2013 do 都省略了 B b(B{a1, a2}); 的复制构造,但没有省略原始 B b({a1 , a2}).

关于c++ - 何时为此结构调用复制构造函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30957087/

有关c++ - 何时为此结构调用复制构造函数?的更多相关文章

  1. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

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

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

  3. 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

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

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

  5. 使用 ACL 调用 upload_file 时出现 Ruby S3 "Access Denied"错误 - 2

    我正在尝试编写一个将文件上传到AWS并公开该文件的Ruby脚本。我做了以下事情:s3=Aws::S3::Resource.new(credentials:Aws::Credentials.new(KEY,SECRET),region:'us-west-2')obj=s3.bucket('stg-db').object('key')obj.upload_file(filename)这似乎工作正常,除了该文件不是公开可用的,而且我无法获得它的公共(public)URL。但是当我登录到S3时,我可以正常查看我的文件。为了使其公开可用,我将最后一行更改为obj.upload_file(file

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

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

  7. c# - 如何在 ruby​​ 中调用 C# dll? - 2

    如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

  8. java - 从 JRuby 调用 Java 类的问题 - 2

    我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www

  9. ruby - 调用其他方法的 TDD 方法的正确方法 - 2

    我需要一些关于TDD概念的帮助。假设我有以下代码defexecute(command)casecommandwhen"c"create_new_characterwhen"i"display_inventoryendenddefcreate_new_character#dostufftocreatenewcharacterenddefdisplay_inventory#dostufftodisplayinventoryend现在我不确定要为什么编写单元测试。如果我为execute方法编写单元测试,那不是几乎涵盖了我对create_new_character和display_invent

  10. ruby - 是否有用于序列化和反序列化各种格式的对象层次结构的模式? - 2

    给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最

随机推荐