【问题标题】:Does guaranteed copy elision work with function parameters?保证复制省略是否适用于函数参数?
【发布时间】:2017-05-29 16:07:56
【问题描述】:

如果我理解正确,从 C++17 开始,这段代码现在要求不进行任何复制:

Foo myfunc(void) {
    return Foo();
}

auto foo = myfunc(); // no copy

函数参数也是如此吗?下面的代码会优化掉副本吗?

Foo myfunc(Foo foo) {
    return foo;
}

auto foo = myfunc(Foo()); // will there be copies?

【问题讨论】:

  • 注意:你使用的是C++17,那如果函数不带参数,为什么还要输入void
  • @Rakete1111 是否有任何标准要求放置void?一定早于我使用 c++ 的时间
  • @tobi303 我不知道。可能在 C++ 标准化之前,当时它仍然基于 C。但我不知道。
  • 嗯,我总是把 void 放在 C 中,我记得 empty 和 void 之间是有区别的(iirc empty 允许任意数量的参数?),我想我已经习惯了 :)跨度>
  • foo(void) 仅在 C 中需要,因为在 C 中 foo() 表示接受任何参数的函数。在 C++ 中,foo() 表示没有参数的函数,foo(void) 仅支持向后兼容。

标签: c++ copy-constructor c++17 object-lifetime copy-elision


【解决方案1】:

在 C++17 中,纯右值(“匿名临时对象”)不再是对象。相反,它们是关于如何构造对象的说明。

他们可以从他们的构造指令中实例化一个临时的,但是由于没有对象那里,没有复制/移动构造可以省略。

Foo myfunc(Foo foo) {
  return foo;
}

所以这里,函数参数foo被移动到了myfunc的prvalue返回值中。您可以从概念上将其视为“myfunc 返回有关如何制作Foo 的说明”。如果您的程序“未使用”这些指令,则会自动实例化一个临时指令并使用这些指令。

auto foo = myfunc(Foo());

所以在这里,Foo() 是一个prvalue。它说“使用() 构造函数构造Foo”。然后用它来构造myfunc 的参数。不发生省略,不调用复制构造函数或移动构造函数,只是()

然后事情发生在myfunc 内部。

myfunc 返回Foo 类型的纯右值。这个prvalue(又名构造指令)用于构造局部变量auto foo

所以这里发生的是Foo 是通过() 构造的,然后移动到auto foo

据我所知,C++14 和 C++17 不支持将函数参数省略为返回值(我可能是错的,我这里没有标准的章节)。但是,当在 return func_arg; 上下文中使用时,它们会被隐式移动。

【讨论】:

    【解决方案2】:

    是的,不是的。引用cppreference:

    在以下情况下,编译器需要省略类对象的复制和移动构造[...]:

    • 在初始化时,如果初始化表达式是prvalue,并且源类型的cv-unqualified版本与目标类是同一个类,则使用初始化表达式初始化目标对象

    • 在函数调用中,如果return语句的操作数是prvalue,并且函数的返回类型与prvalue的类型相同。

    因此,在您的第二个 sn-p 中,只会调用一个默认构造函数。首先,myFunc中的foo是从Foo()初始化的(1个默认构造),是prvalue。这意味着它将被省略(参见第 1 点)。

    接下来,myFunc 返回foo 的副本,因为foo 不是prvalue(第2 点),所以不能省略。因此,采取了一项措施,因为 foo 是一个 xvalue。但是实际的返回值是一个纯右值,因为它是foo 的一个新实例(在myFunc 中),并且由于第一个点,它被省略了。

    总之,标准保证了一种默认构造和一种移动。不能再有了。但是,编译器实际上可能完全忽略了唯一的动作。

    【讨论】:

    • 复制?我想你的意思是移动。 C++ 标准中的省略构造指的是不同于 as-if 规则的东西。你的意思是使用省略?
    • 而 C++17 使某些类型的东西不是对象;不会发生省略的移动/复制。
    • @Yakk 是的,你是对的! :) 但我不明白你关于非对象的观点。能详细点吗?
    • 在 C++14 中,纯右值是临时对象。在 C++17 中,纯右值是另外一回事。基本上,有关如何构造对象的说明。在某些上下文中,临时对象可以从纯右值中具体化,但它们也可以用于直接构造对象。见this SO answer
    • “但是,如果移动构造函数没有副作用,编译器实际上可能会完全忽略唯一的移动。”呃,不是复制/移动的基本点省略它们是是否有副作用无关紧要的唯一上下文,因为允许省略所述副作用?
    猜你喜欢
    • 2021-08-21
    • 1970-01-01
    • 2018-01-23
    • 1970-01-01
    • 1970-01-01
    • 2015-09-30
    • 1970-01-01
    • 2023-01-30
    • 1970-01-01
    相关资源
    最近更新 更多