在C++ 03中,表达式是rvalue或lvalue.
在C++ 11中,表达式可以是:
两类已成为五大类.
我听过斯科特迈耶斯说" std::move()什么都不动"......但我还没明白这意味着什么.
所以要指出我的问题,请考虑以下事项:
class Box { /* things... */ };
Box box1 = some_value;
Box box2 = box1; // value of box1 is copied to box2 ... ok
Run Code Online (Sandbox Code Playgroud)
关于什么:
Box box3 = std::move(box1);
Run Code Online (Sandbox Code Playgroud)
我确实理解左值和左值的规则,但我不明白的是记忆中实际发生了什么?它只是以某种不同的方式复制价值,共享地址或什么?更具体地说:什么使移动比复制更快?
我只是觉得理解这一点会让一切都清楚.提前致谢!
编辑:请注意,我不是在询问std::move()实现或任何语法的东西.
当给出以下结构的代码时
template <typename... Args>
void foo(Args&&... args) { ... }
Run Code Online (Sandbox Code Playgroud)
我经常static_cast<Args&&>在函数中看到库代码用于参数转发.通常,对此的理由是使用static_cast避免不必要的模板实例化.
鉴于语言的参考折叠和模板扣除规则.我们得到了完美的转发,static_cast<Args&&>这个声明的证据如下(在误差范围内,我希望答案会启发)
&& &&- > &&(1上面的规则)& &&- > &(2上面的规则)这基本上foo()是bar()在上面的例子中转发参数.这也是你在std::forward<Args>这里使用时会得到的行为.
问题 -为什么要std::forward在这些背景下使用?避免额外的实例化是否有理由违反惯例?
Howard Hinnant的论文n2951规定了6个约束,在这些约束条件下,任何实现都std::forward应该"正确".这些曾经是
(1)和(2)证明与static_cast<Args&&>上述方法一起正常工作.(3) - (6)这里不适用,因为在推导的上下文中调用函数时,这些都不会发生.
注意:我个人更喜欢使用std::forward,但我的理由纯粹是我更喜欢坚持惯例.
请考虑以下代码:
struct MyStruct
{
int iInteger;
string strString;
};
void MyFunc(vector<MyStruct>& vecStructs)
{
MyStruct NewStruct = { 8, "Hello" };
vecStructs.push_back(std::move(NewStruct));
}
int main()
{
vector<MyStruct> vecStructs;
MyFunc(vecStructs);
}
Run Code Online (Sandbox Code Playgroud)
为什么这样做?
在调用MyFunc时,返回地址应放在当前线程的堆栈上.现在创建NewStruct对象,它也应该被放置在堆栈上.使用std :: move,我告诉编译器,我不打算再使用NewStruct引用了.他可以偷走记忆.(push_back函数是具有移动语义的函数.)
但是当函数返回并且NewStruct超出范围时.即使编译器不会从堆栈中删除最初存在的结构所占用的内存,他至少也要删除先前存储的返回地址.
这将导致碎片堆栈,未来的分配将覆盖"移动"的内存.
有人可以向我解释一下吗?
编辑:首先:非常感谢您的回答.但是从我学到的东西,我仍然无法理解,为什么以下不能像我期望的那样工作:
struct MyStruct
{
int iInteger;
string strString;
string strString2;
};
void MyFunc(vector<MyStruct>& vecStructs)
{
MyStruct oNewStruct = { 8, "Hello", "Definetly more than 16 characters" };
vecStructs.push_back(std::move(oNewStruct));
// At this point, oNewStruct.String2 should be "", because its memory was stolen.
// But only …Run Code Online (Sandbox Code Playgroud) 该功能std::move()定义为
template<typename T>
typename std::remove_reference<T>::type&& move(T && t)
{
return static_cast<typename std::remove_reference<T>::type&&>( t );
}
Run Code Online (Sandbox Code Playgroud)
有四个地方我可以想象要调用的移动构造函数:
std::move()函数本身,但可能在返回的引用最终到达的位置.我敢打赌4号,但我不是百分百肯定,所以请解释你的答案.
我只是比较将字符串传递给函数的性能。该基准测试结果很有趣。
这是我的代码:
void add(std::string msg)
{
msg += "world";
}
void addRvalue(std::string&& msg)
{
msg += "world";
}
void addRef(std::string& msg)
{
msg += "world";
}
void StringCreation() {
add(std::string("hello "));
}
void StringCopy() {
std::string msg("hello ");
add(msg);
}
void StringMove() {
std::string msg("hello ");
add(std::move(msg));
}
void StringRvalue() {
std::string msg("hello ");
addRvalue(std::move(msg));
}
void StringReference() {
std::string msg("hello ");
addRef(msg);
}
Run Code Online (Sandbox Code Playgroud)
StringCreation(),StringRvalue()和StringReference()是等效的。我很惊讶StringMove()是性能最低的-比涉及复制的值传递差。
我是否认为调用StringMove()涉及一个移动构造函数,然后在调用add()时涉及一个复制构造函数,对吗?它不仅仅涉及一举动的构造函数吗?我以为搬家的建设很便宜。
我增加了传递给add()的字符串的长度,这确实有所作为。现在,StringMove()仅比StringCreation和StringReference慢1.1倍。StringCopy现在是最糟糕的,这正是我所期望的。
这是新的基准测试结果。
因此,StringMove根本不涉及复制-仅适用于小字符串。
C++ 11引入了一个新的rvalue引用概念.我正在某处读它,发现如下:
class Base
{
public:
Base() //Default Ctor
Base(int t) //Parameterized Ctor
Base(const Base& b) //Copy Ctor
Base(Base&& b) //Move Ctor
};
void foo(Base b) //Function 1
{}
void foo(Base& b) //Function 2
{}
int main()
{
Base b(10);
foo(b); -- Line 1 (i know of ambiquity but lets ignore for understanding purpose)
foo(Base()); -- Line 2
foo(2) ; -- Line 3
}
Run Code Online (Sandbox Code Playgroud)
现在我的理解有限,我的观察结果如下:
第1行将简单地调用复制构造函数,因为参数是左值.
在C++ 11之前的第2行会调用复制构造函数和所有那些临时复制内容,但是定义了移动构造函数,这里将调用它.
第3行将再次调用move构造函数,因为2将隐式转换为Base类型(rvalue).
请纠正并解释上述任何观察结果是否错误.
现在,这是我的问题:
我知道一旦我们移动一个物体,它的数据就会在呼叫位置丢失.所以,我在上面的例子中如何更改第2行以在foo中移动对象"b"(是否使用std :: move(b)?).
我读过移动构造函数比复制构造函数更有效.怎么样?我只能想到在移动构造函数的情况下我们在堆上有内存的情况不需要再分配.当我们在堆上没有任何内存时,这个陈述是否成立?
它是否比通过引用传递更有效(不,对吧?)?
class foo{
public:
bar steal_the_moveable_object();
private:
bar moveable_object;
};
main(){
foo f;
auto moved_object= f.steal_the_moveable_object();
}
Run Code Online (Sandbox Code Playgroud)
如何实现steal_the_movebale_object移动moveable_object到moved_object?
编辑:考虑以下两个例子:
std::string x;
{
std::string y = "extremely long text ...";
...
x = y; // *** (1)
}
do_something_with(x);
Run Code Online (Sandbox Code Playgroud)
struct Y
{
Y();
Y(const Y&);
Y(Y&&);
... // many "heavy" members
};
struct X
{
X(Y y) : y_(std::move(y)) { }
Y y_;
}
X foo()
{
Y y;
...
return y; // *** (2)
}
Run Code Online (Sandbox Code Playgroud)
y在线(1)和(2)的两个例子中,它的寿命接近结束并且即将被破坏.很明显,它可以被视为右值并在两种情况下都可以移动.在(1)中,它的内容可以移入x和移入(2)到temp的实例中X().y_.
我的问题是:
1)是否会在上述任何一个示例中移动?(a)如果是,则采用何种标准规定.(b)如果不是,为什么不呢?这是标准中的遗漏还是我没有想到的另一个原因?
2)如果上述答案为否.在第一个例子中,我可以改变(1)x = std::move(y)以强制编译器执行移动.在第二个示例中,我可以做什么来向编译器指示y可以移动?return std::move(y)?
注意:我故意返回(2)中的实例Y而不是X …
using Ptr = std::unique_ptr<int>;
Ptr f(bool arg) {
std::list<Ptr> list;
Ptr ptr(new int(1));
list.push_back(std::move(ptr));
if (arg) {
Ptr&& obj1 = std::move(list.front());
// Here |obj1| and |list.front()| still point to the same location!
list.pop_front();
return std::move(obj1);
}
else {
Ptr obj2 = std::move(list.front());
list.pop_front();
return obj2;
}
};
Ptr&& ptr1 = f(true); // |ptr1| is empty.
Ptr&& ptr2 = f(false); // |ptr2| is fine.
Run Code Online (Sandbox Code Playgroud)
完整来源在这里。
我不明白-为什么obj1,并list.front()仍然指向相同的位置后,std::move()被称为?
c++ ×10
c++11 ×7
move ×2
c++-faq ×1
c++14 ×1
c++17 ×1
expression ×1
forwarding ×1
rvalue ×1
stdstring ×1
templates ×1
unique-ptr ×1