RVO 是否适用于对象成员?
Does RVO work on object members?
考虑以下几点:
struct A { /* ... */ };
A foo() {
auto p = std::make_pair(A{}, 2);
// ... do something
return p.first;
}
auto a = foo();
p.first
会被复制、移动或 RVO 编辑吗?
我发现 Visual Studio 2010 和 gcc-5.1 RVO 未 应用(参见示例 http://coliru.stacked-crooked.com/a/17666dd9e532da76)。
标准的相关部分是 12.8.31.1 [class.copy]。它声明允许复制省略(我的突出显示):
in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function parameter or a variable introduced by the exception-declaration of a handler ([except.handle])) with the same type (ignoring cv-qualification) as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function's return value
由于p.first
不是对象名,禁止RVO
@atkins 首先得到了答案。只需添加这个小测试程序,您将来在跟踪 move/assign 行为时可能会发现它很有用。
#include <iostream>
#include <string>
using namespace std::string_literals;
struct A {
A()
: history("created")
{
}
A(A&& r)
: history("move-constructed,"s + r.history)
{
r.history = "zombie: was "s + r.history;
}
A(const A& r)
: history("copied from: " + r.history)
{
}
~A() {
history = "destroyed,"s + history;
std::cout << history << std::endl;
}
A& operator=(A&& r) {
history = "move-assigned from " + r.history + " (was "s + history + ")"s;
r.history = "zombie: was "s + r.history;
return *this;
}
A& operator=(const A&r ) {
history = "copied from " + r.history;
return *this;
}
std::string history;
};
A foo() {
auto p = std::make_pair(A{}, 2);
// ... do something
return p.first;
}
auto main() -> int
{
auto a = foo();
return 0;
}
示例输出:
destroyed,zombie: was created
destroyed,move-constructed,created
destroyed,copied from: move-constructed,created
只是为了增加一点燃料,如果 RVO 在发挥作用,这个功能会怎样?调用者已将 A
的实例放在内存中的某个位置,然后调用 foo
对其进行分配(更好的是,假设 A
是更大结构的一部分,让我们假设它正确对齐,使得结构的下一个成员紧跟在 A
的那个实例之后)。假设 RVO 正在运行,p
的 first
部分位于调用者想要的位置,但是 int
即 second
放置在哪里?它必须紧跟在 A
的实例之后,以保持 pair
正常运行,但在源位置,在 A
的实例之后还有一些其他成员。
我希望 RVO 不会在这个地方发生,因为您只返回一个较大对象的一部分。可能会发生移动,因为 first
必须处于可破坏状态。
考虑以下代码:
struct A {};
struct B {};
struct C { B c[100000]; };
A callee()
{
struct S
{
A a;
C c;
} s;
return s.a;
}
void caller()
{
A a = callee();
// here should lie free unused spacer of size B[100000]
B b;
}
"Partial" RVO 应该会导致调用者过度使用堆栈膨胀,因为(我认为)S
只能完全在调用者堆栈框架中构建。
另一个问题是 ~S()
行为:
// a.hpp
struct A {};
struct B {};
struct C { A a; B b; ~C(); };
// a.cpp
#include "a.hpp"
~C() { /* ... */; }
// main.cpp
#include "a.hpp"
A callee()
{
C c;
return c.a;
} // How to destruct c partially, having the user defined ~C() in another TU?
// Even if destructor is inline and its body is visible,
// how to automatically change its logic properly?
// It is impossible in general case.
void caller() { A a = callee(); }
考虑以下几点:
struct A { /* ... */ };
A foo() {
auto p = std::make_pair(A{}, 2);
// ... do something
return p.first;
}
auto a = foo();
p.first
会被复制、移动或 RVO 编辑吗?
我发现 Visual Studio 2010 和 gcc-5.1 RVO 未 应用(参见示例 http://coliru.stacked-crooked.com/a/17666dd9e532da76)。
标准的相关部分是 12.8.31.1 [class.copy]。它声明允许复制省略(我的突出显示):
in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function parameter or a variable introduced by the exception-declaration of a handler ([except.handle])) with the same type (ignoring cv-qualification) as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function's return value
由于p.first
不是对象名,禁止RVO
@atkins 首先得到了答案。只需添加这个小测试程序,您将来在跟踪 move/assign 行为时可能会发现它很有用。
#include <iostream>
#include <string>
using namespace std::string_literals;
struct A {
A()
: history("created")
{
}
A(A&& r)
: history("move-constructed,"s + r.history)
{
r.history = "zombie: was "s + r.history;
}
A(const A& r)
: history("copied from: " + r.history)
{
}
~A() {
history = "destroyed,"s + history;
std::cout << history << std::endl;
}
A& operator=(A&& r) {
history = "move-assigned from " + r.history + " (was "s + history + ")"s;
r.history = "zombie: was "s + r.history;
return *this;
}
A& operator=(const A&r ) {
history = "copied from " + r.history;
return *this;
}
std::string history;
};
A foo() {
auto p = std::make_pair(A{}, 2);
// ... do something
return p.first;
}
auto main() -> int
{
auto a = foo();
return 0;
}
示例输出:
destroyed,zombie: was created
destroyed,move-constructed,created
destroyed,copied from: move-constructed,created
只是为了增加一点燃料,如果 RVO 在发挥作用,这个功能会怎样?调用者已将 A
的实例放在内存中的某个位置,然后调用 foo
对其进行分配(更好的是,假设 A
是更大结构的一部分,让我们假设它正确对齐,使得结构的下一个成员紧跟在 A
的那个实例之后)。假设 RVO 正在运行,p
的 first
部分位于调用者想要的位置,但是 int
即 second
放置在哪里?它必须紧跟在 A
的实例之后,以保持 pair
正常运行,但在源位置,在 A
的实例之后还有一些其他成员。
我希望 RVO 不会在这个地方发生,因为您只返回一个较大对象的一部分。可能会发生移动,因为 first
必须处于可破坏状态。
考虑以下代码:
struct A {};
struct B {};
struct C { B c[100000]; };
A callee()
{
struct S
{
A a;
C c;
} s;
return s.a;
}
void caller()
{
A a = callee();
// here should lie free unused spacer of size B[100000]
B b;
}
"Partial" RVO 应该会导致调用者过度使用堆栈膨胀,因为(我认为)S
只能完全在调用者堆栈框架中构建。
另一个问题是 ~S()
行为:
// a.hpp
struct A {};
struct B {};
struct C { A a; B b; ~C(); };
// a.cpp
#include "a.hpp"
~C() { /* ... */; }
// main.cpp
#include "a.hpp"
A callee()
{
C c;
return c.a;
} // How to destruct c partially, having the user defined ~C() in another TU?
// Even if destructor is inline and its body is visible,
// how to automatically change its logic properly?
// It is impossible in general case.
void caller() { A a = callee(); }