保证复制省略在函数参数中是否有效?

8

如果我理解正确的话,从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?

9
注意:您正在使用C++17,那么如果函数不需要参数,为什么还要使用void - Rakete1111
1
@Rakete1111,是否有任何标准要求放置那个 void?这可能是在我接触C++之前很久的事情了。 - 463035818_is_not_a_number
@tobi303 我不知道。可能是在C++标准化之前,当它仍然基于C时。但我不确定。 - Rakete1111
哦,我总是写 void - 在 C 中,我记得空和 void 之间有区别(如果我没记错的话,空允许任意数量的参数?),我想我习惯了 :) - Maël Nison
4
在C语言中,foo(void)是必需的,因为在C中,foo()表示接受任何参数的函数。而在C++中,foo()表示不带参数的函数,foo(void)仅用于向后兼容。 - rustyx
2个回答

8
在C++17中,prvalues(“匿名临时对象”)不再是对象。相反,它们是构建对象的指示。
它们可以从其构造指令实例化临时对象,但由于没有对象在那里,因此不存在要省略的复制/移动构造。
Foo myfunc(Foo foo) {
  return foo;
}

因此,在这里,函数参数foo会被移动到myfunc的prvalue返回值中。 您可以从概念上将其视为“myfunc返回有关如何制作Foo的说明”。 如果您的程序“未使用”这些说明,则会自动实例化临时对象并使用这些说明。(顺便说一下,这些说明包括时间旅行条款;构造的时间点仍然在函数内部!)
auto foo = myfunc(Foo());

这里,Foo() 是一个 prvalue。它表示“使用 () 构造函数构造一个 Foo”。然后用它来构造 myfunc 的参数。没有省略,也没有调用拷贝构造函数或移动构造函数,只有 ()myfunc 中发生了其他事情。 myfunc 返回一个类型为 Foo 的 prvalue。这个 prvalue(也就是构造指令)被用于构造本地变量 auto foo
因此,这里发生的是通过 () 构造了一个 Foo,然后将其移动到 auto foo 中。
C++14 和 C++17 不支持函数参数到返回值的省略(如果我没记错的话,我没有标准的章节和课文在这里)。但是,在 return func_arg; 上下文中使用时,它们会被隐式移动。

2
是和不是。引用cppreference的话:
在以下情况下,编译器需要省略类对象的复制和移动构造函数[...]:
- 如果在初始化中,初始化表达式是prvalue,并且源类型的cv-unqualified版本与目标类型的类相同,则使用初始化表达式初始化目标对象。 - 在函数调用中,如果返回语句的操作数是prvalue,并且函数的返回类型与该prvalue的类型相同。
因此,在您的第二个片段中,只会调用一个默认构造函数。首先,在myFunc中,fooFoo()(1个默认构造)初始化。这意味着它将被省略(参见点1)。
接下来,myFunc 返回一个foo的副本,由于foo不是prvalue(第二点),因此无法省略。所以,由于foo是xvalue,会进行一次move操作。但实际返回值是prvalue,因为它是myFuncfoo的新实例,并且由于第一点,它被省略了。
总之,标准保证了一个默认构造和一个move。不能再有更多了。但是,编译器可能会完全省略这唯一的move。

2
复制?我想你是指移动。在C++标准中,省略构造函数指的是与as-if规则不同的事情;你的意思是使用省略吗? - Yakk - Adam Nevraumont
而C++17使某些类型的事情不再是对象;不会发生省略移动 / 复制。 - Yakk - Adam Nevraumont
@Yakk 是的,你说得对!:) 但是我不明白你关于非对象的观点。你能否详细说明一下? - Rakete1111
3
在C++14中,prvalue是临时对象。在C++17中,prvalue是另一种东西;基本上是构造对象的指令。在某些情况下,可以通过prvalue来实现临时对象,但它们也可用于直接构造对象。请参见此SO答案。 - Yakk - Adam Nevraumont
但是,如果移动构造函数没有副作用,编译器实际上可能完全省略唯一的移动。嗯,复制/移动省略的一个基本要点不就是它们是唯一的上下文,在这种情况下“是否存在副作用并不重要”,因为允许省略所述副作用? - underscore_d

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接