Joh*_*ata 33 c++ move-semantics c++11
我试图理解移动语义,右值引用std::move等等.我一直试图通过搜索本网站上的各种问题来解决,如果需要复制,为什么传递const std::string &name+ _name(name)不如std::string name+ + _name(std::move(name)).
如果我理解正确,下面需要一个副本(通过构造函数)加上一个移动(从临时到成员):
Dog::Dog(std::string name) : _name(std::move(name)) {}
Run Code Online (Sandbox Code Playgroud)
替代(和老式)方法是通过引用传递它并将其复制(从引用到成员):
Dog::Dog(const std::string &name) : _name(name) {}
Run Code Online (Sandbox Code Playgroud)
如果第一种方法需要复制并同时移动两种方法,而第二种方法只需要一个副本,那么第一种方法如何首选,在某些情况下更快?
Die*_*ühl 31
在使用数据时,您需要一个可以使用的对象.当你得到a时,std::string const&你将不得不独立于是否需要参数来复制对象.
当对象按值传递时,如果必须复制对象,则对象将被复制,即,当传递的对象不是临时对象时.但是,如果它恰好是临时的,则可以在适当的位置构建对象,即,任何副本都可能已被删除,您只需支付移动结构.也就是说,有可能实际上没有副本发生.
Jef*_*ett 27
考虑使用左值和右值调用各种选项:
Dog::Dog(const std::string &name) : _name(name) {}
Run Code Online (Sandbox Code Playgroud)
无论是所谓的与左值右值或者,这需要一份副本,初始化_name从name.移动不是一种选择,因为name是const.
Dog::Dog(std::string &&name) : _name(std::move(name)) {}
Run Code Online (Sandbox Code Playgroud)
这只能用右值调用,它会移动.
Dog::Dog(std::string name) : _name(std::move(name)) {}
Run Code Online (Sandbox Code Playgroud)
使用左值调用时,将复制以传递参数,然后移动以填充数据成员.使用rvalue调用时,将移动以传递参数,然后移动以填充数据成员.在rvalue的情况下,可以省略移动以传递参数.因此,使用左值调用此结果会导致一个副本和一个移动,并使用右值调用此结果会导致一到两个移动.
最佳解决方案是定义(1)和(2).解决方案(3)可以相对于最佳值进行额外移动.但是编写一个函数比编写两个几乎相同的函数更短,更易于维护,并且假设移动很便宜.
当使用可隐式转换为字符串的值进行调用时const char*,会发生隐式转换,其涉及长度计算和字符串数据的副本.然后我们陷入了右边的情况.在这种情况下,使用a string_view提供了另一种选择:
Dog::Dog(std::string_view name) : _name(name) {}
Run Code Online (Sandbox Code Playgroud)
使用字符串左值或右值调用时,会产生一个副本.当用a调用时const char*,进行一次长度计算和一次复制.
简短回答:由const调用并且总是花费一份副本.根据条件,按值调用可能只需要一次移动.但这取决于(请参阅下面的代码示例,了解此表所引用的场景):
lvalue rvalue unused lvalue unused rvalue
------------------------------------------------------
const& copy copy - -
rvalue&& - move - -
value copy, move move copy -
T&& copy move - -
overload copy move - -
Run Code Online (Sandbox Code Playgroud)
因此,我的执行摘要将是值得考虑的价值
考虑一个用于复制其参数的函数
class Dog {
public:
void name_it(const std::string& newName) { names.push_back(newName); }
private:
std::vector<std::string> names;
};
Run Code Online (Sandbox Code Playgroud)
如果传递了左值name_it,则在rvalue的情况下,您将进行两次复制操作.这很糟糕,因为右手可以让我感动.
一种可能的解决方案是为rvalues写一个重载:
class Dog {
public:
void name_it(const std::string& newName) { names.push_back(newName); }
void name_it(std::string&& newName) { names.push_back(std::move(newName)); }
private:
std::vector<std::string> names;
};
Run Code Online (Sandbox Code Playgroud)
这解决了问题,一切都很好,尽管你有两个代码两个函数具有完全相同的代码.
另一个可行的解决方案是使用完美转发,但也有一些缺点,(例如,完美的转发功能非常贪婪,并且使现有的重载const和函数无用,通常它们需要在头文件中,它们创建了几个函数目标代码等等.)
class Dog {
public:
template<typename T>
void name_it(T&& in_name) { names.push_back(std::forward<T>(in_name)); }
private:
std::vector<std::string> names;
};
Run Code Online (Sandbox Code Playgroud)
另一种解决方案是使用按值调用:
class Dog {
public:
void name_it(std::string newName) { names.push_back(std::move(newName)); }
private:
std::vector<std::string> names;
};
Run Code Online (Sandbox Code Playgroud)
重要的是,正如你提到的那样std::move.这样,rvalue和lvalue都有一个函数.您将移动右值但接受左值的额外移动,如果移动便宜并且无论条件如何都复制或移动参数,这可能没问题.
所以最后我真的认为推荐一种方式胜过其他方式是完全错误的.这很大程度上取决
#include <vector>
#include <iostream>
#include <utility>
using std::cout;
class foo{
public:
//constructor
foo() {}
foo(const foo&) { cout << "\tcopy\n" ; }
foo(foo&&) { cout << "\tmove\n" ; }
};
class VDog {
public:
VDog(foo name) : _name(std::move(name)) {}
private:
foo _name;
};
class RRDog {
public:
RRDog(foo&& name) : _name(std::move(name)) {}
private:
foo _name;
};
class CRDog {
public:
CRDog(const foo& name) : _name(name) {}
private:
foo _name;
};
class PFDog {
public:
template <typename T>
PFDog(T&& name) : _name(std::forward<T>(name)) {}
private:
foo _name;
};
//
volatile int s=0;
class Dog {
public:
void name_it_cr(const foo& in_name) { names.push_back(in_name); }
void name_it_rr(foo&& in_name) { names.push_back(std::move(in_name));}
void name_it_v(foo in_name) { names.push_back(std::move(in_name)); }
template<typename T>
void name_it_ur(T&& in_name) { names.push_back(std::forward<T>(in_name)); }
private:
std::vector<foo> names;
};
int main()
{
std::cout << "--- const& ---\n";
{
Dog a,b;
foo my_foo;
std::cout << "lvalue:";
a.name_it_cr(my_foo);
std::cout << "rvalue:";
b.name_it_cr(foo());
}
std::cout << "--- rvalue&& ---\n";
{
Dog a,b;
foo my_foo;
std::cout << "lvalue: -\n";
std::cout << "rvalue:";
a.name_it_rr(foo());
}
std::cout << "--- value ---\n";
{
Dog a,b;
foo my_foo;
std::cout << "lvalue:";
a.name_it_v(my_foo);
std::cout << "rvalue:";
b.name_it_v(foo());
}
std::cout << "--- T&&--\n";
{
Dog a,b;
foo my_foo;
std::cout << "lvalue:";
a.name_it_ur(my_foo);
std::cout << "rvalue:";
b.name_it_ur(foo());
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
输出:
--- const& ---
lvalue: copy
rvalue: copy
--- rvalue&& ---
lvalue: -
rvalue: move
--- value ---
lvalue: copy
move
rvalue: move
--- T&&--
lvalue: copy
rvalue: move
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
4517 次 |
| 最近记录: |