【发布时间】:2014-05-29 22:10:00
【问题描述】:
我正在尝试获取此类的 rvalue 实例:
#include <iostream>
#define msg(x) std::cout << x " constructor\n"
struct X {
int i;
X(int i) : i(i) {msg("X");}
X(const X& x) : i(x.i) {std::cout << "X copy\n";}
X(X&& x) {std::swap(i, x.i); std::cout << "X move\n";}
};
进入该类的实例变量x:
struct A {
X x;
A(X x) : x(x) {msg("A");}
};
像这样:
int main() {
A a(X(1));
std::cout << a.x.i << "\n\n";
}
不进行任何复制或移动。
根据这些参考资料,
- http://thbecker.net/articles/rvalue_references/section_01.html
- http://www.slideshare.net/oliora/hot-11-2-new-style-arguments-passing
还有很多关于 SO 的帖子(所以请在标记为重复之前阅读到最后),我应该依赖 复制省略,如果我应该满足其条件按值传递。请注意,需要两个复制省略,即:
constructor call -> constructor local variable -> instance variable
在关闭复制省略时可以看到(使用 g++-4.8 -std=c++11 -fno-elide-constructors 编译):
X constructor
X move
X copy
A constructor
1
所以有一个move 步骤和一个copy 步骤,如果我打开复制省略(使用g++-4.8 -std=c++11 -O3 编译),它们都应该消失:
X constructor
X copy
A constructor
1
糟糕,copy 步骤仍然存在!
我可以通过std::move()、std::forward 的任何其他变体或传递为rvalue-reference 来获得更好的结果吗?
struct B {
X x;
B(X x) : x(std::move(x)) {msg("B");}
};
struct C {
X x;
C(X x) : x(std::forward<X>(x)) {msg("C");}
};
struct D {
X x;
D(X&& x) : x(std::move(x)) {msg("D");}
};
int main() {
B b(X(2));
std::cout << b.x.i << "\n\n";
C c(X(3));
std::cout << c.x.i << "\n\n";
D d(X(4));
std::cout << d.x.i << "\n\n";
}
产生输出:
X constructor
X move
B constructor
2
X constructor
X move
C constructor
3
X constructor
X move
D constructor
4
好的,我把copy变成了move,但这并不令人满意!
接下来,我尝试将实例变量x 设为引用X&:
struct E {
X& x;
E(X x) : x(x) {msg("E");}
};
int main() {
E e(X(5));
std::cout << e.x.i << "\n\n";
}
产生:
X constructor
E constructor
1690870696
坏主意!我摆脱了move,但x 引用的rvalue 实例在我的座位下被破坏,所以最后一行打印垃圾而不是5。两个注意事项:
-
g++-4.8没有警告我任何事情,即使是-pedantic -Wall -Wextra - 程序在使用
-O0编译时打印5
所以这个错误可能会在很长一段时间内被忽视!
那么,这是一个没有希望的案例吗?好吧不:
struct F {
X& x;
F(X& x) : x(x) {msg("F");}
};
int main() {
X x(6);
F f(x);
std::cout << f.x.i << "\n";
}
打印:
X constructor
F constructor
6
真的吗?没有花哨的新 C++11 功能,编译器不会自行决定复制省略,只是普通的旧 FORTRAN66 样式的传递引用可以满足我的需求并且可能会表现最好?
这是我的问题:
- 有什么方法可以让
rvalues工作吗?我错过了任何功能吗? -
lvalue-reference 版本真的是最好的吗,还是X x(6)步骤中存在隐藏成本? -
x在f建成后生活会不会有什么不便? - 我可以为使用
lvalue引用外部实例支付数据局部性罚款吗?
【问题讨论】:
-
struct E具有未定义的行为(悬空引用。)和struct F“有效”,但与其他所有行为完全不同(除了 UBE)。那么你想比较什么? -
@juanchopanza: 是的,我学会了
struct E的行为。 -
最好的选择是两个重载:
T1(const T2& t) : t_(t) {}和T1(T2&& t) : t_(std::move(t)) {} -
@juanchopanza:所有版本,包括
struct F,都可以完全访问实例x,这是我需要的。 -
不,
F引用了外部实体。它不拥有它。其他人(忽略E)可以。这是一个至关重要的区别。
标签: c++ c++11 rvalue-reference move-constructor copy-elision