我应该始终为函数和方法实现移动语义吗?
Should I always implement a move-semantic for functions and methods?
用 move 重载 set(/*args*/)
是否有意义?:
//example:
class Person {
private:
//properties
std::string name;
std::string address;
std::string favmovie;
public:
//set without operator=
void set(const std::string& name, const std::string& address, const std::string& favmovie) {
this->name = name;
this->address = address;
this->favmovie = favmovie;
return;
}
//set without operator=
void set(std::string&& name, std::string&& address, std::string&& favmovie) {
this->name = std::move(name);
this->address = std::move(address);
this->favmovie = std::move(favmovie);
return;
}
Person(const std::string& name, const std::string& address, const std::string& favmovie)
: name(name), address(address), favmovie(favmovie) {
}
Person(std::string&& name, std::string&& address, std::string&& favmovie)
: name(std::move(name)), address(std::move(address)), favmovie(std::move(favmovie)) {
}
};
感觉就像复制和粘贴并进行一些编辑,但我对每个功能或方法都这样做,目的是为了使它们具有高性能。但这是一个好习惯吗?
如果函数的目的本身并不能从移动语义中获益,并且函数不能从对可变参数的引用中获益,则没有理由使用将右值引用作为参数的重载版本。
对于这里显示的简单函数,答案显然是否定的。但是不难想到函数将其参数存储在某个地方的示例。在那种情况下,拥有一个利用移动语义的重载版本,而不是为了存储它而必须复制构造对象,将会有明显的好处。
这一切都归结为函数需要其参数的目的。
这是您使用按值传递的地方:
void set(std::string name, std::string adress, std::string favmovie)
{
this->name = std::move(name);
this->adress = std::move(adress);
this->favmovie = std::move(favmovie);
}
然后,如果参数是右值,它将被移动到参数中,然后移动到 this
- 两次移动,没有副本。而对于 const 左值引用版本,总是有一个副本。对于左值参数的情况,两种情况下都有 1 个副本(在值版本中有 1 个额外的移动,但移动很便宜,无论如何都可以省略)。
@M.M 的答案有一个缺点,即每次调用 set
时都会重新分配存储,而通过引用传递参数将允许数据简单地进行复制分配而无需重新分配只要它们的容量已经足以容纳通过参数传入的数据。有关详细信息,请参阅 Herb Sutter's CppCon talk。所以我会推荐
void set(const std::string &name, const std::string &adress, const std::string &favmovie)
{
this->name = name;
this->adress = adress;
this->favmovie = favmovie;
}
构造函数可以使用聪明的按值传递技巧,但除非它是真正的低级代码,否则优化可能不值得,IMO。
用 move 重载 set(/*args*/)
是否有意义?:
//example:
class Person {
private:
//properties
std::string name;
std::string address;
std::string favmovie;
public:
//set without operator=
void set(const std::string& name, const std::string& address, const std::string& favmovie) {
this->name = name;
this->address = address;
this->favmovie = favmovie;
return;
}
//set without operator=
void set(std::string&& name, std::string&& address, std::string&& favmovie) {
this->name = std::move(name);
this->address = std::move(address);
this->favmovie = std::move(favmovie);
return;
}
Person(const std::string& name, const std::string& address, const std::string& favmovie)
: name(name), address(address), favmovie(favmovie) {
}
Person(std::string&& name, std::string&& address, std::string&& favmovie)
: name(std::move(name)), address(std::move(address)), favmovie(std::move(favmovie)) {
}
};
感觉就像复制和粘贴并进行一些编辑,但我对每个功能或方法都这样做,目的是为了使它们具有高性能。但这是一个好习惯吗?
如果函数的目的本身并不能从移动语义中获益,并且函数不能从对可变参数的引用中获益,则没有理由使用将右值引用作为参数的重载版本。
对于这里显示的简单函数,答案显然是否定的。但是不难想到函数将其参数存储在某个地方的示例。在那种情况下,拥有一个利用移动语义的重载版本,而不是为了存储它而必须复制构造对象,将会有明显的好处。
这一切都归结为函数需要其参数的目的。
这是您使用按值传递的地方:
void set(std::string name, std::string adress, std::string favmovie)
{
this->name = std::move(name);
this->adress = std::move(adress);
this->favmovie = std::move(favmovie);
}
然后,如果参数是右值,它将被移动到参数中,然后移动到 this
- 两次移动,没有副本。而对于 const 左值引用版本,总是有一个副本。对于左值参数的情况,两种情况下都有 1 个副本(在值版本中有 1 个额外的移动,但移动很便宜,无论如何都可以省略)。
@M.M 的答案有一个缺点,即每次调用 set
时都会重新分配存储,而通过引用传递参数将允许数据简单地进行复制分配而无需重新分配只要它们的容量已经足以容纳通过参数传入的数据。有关详细信息,请参阅 Herb Sutter's CppCon talk。所以我会推荐
void set(const std::string &name, const std::string &adress, const std::string &favmovie)
{
this->name = name;
this->adress = adress;
this->favmovie = favmovie;
}
构造函数可以使用聪明的按值传递技巧,但除非它是真正的低级代码,否则优化可能不值得,IMO。