我目前正在学习 C++,并尽量避免养成坏习惯。据我了解,clang-tidy 包含许多“最佳实践”,我尽量坚持使用它们(尽管我不一定明白 为什么 它们被认为是好的),但我不确定我是否了解这里推荐的内容。
我使用了教程中的这个类:
class Creature { private: std::string m_name; public: Creature(const std::string &name) : m_name{name} { } };
这导致了 clang-tidy 的建议,即我应该通过 value 而不是 reference 和 use 传递std::move。如果我这样做了,我会收到建议进行name引用(以确保它不会每次都被复制)以及std::move不会产生任何影响的警告,因为name是一个const所以我应该删除它。
std::move
name
const
我没有收到警告的唯一方法是const完全删除:
Creature(std::string name) : m_name{std::move(name)} { }
这似乎是合乎逻辑的,因为唯一的好处const是防止弄乱原始字符串(这不会发生,因为我按值传递)。但我在CPlusPlus.com上读到:
尽管请注意 - 在标准库中 - 移动意味着被移动的对象处于有效但未指定的状态。这意味着,在这样的操作之后,被移动对象的值应该只被销毁或分配一个新值;否则访问它会产生一个未指定的值。
现在想象一下这段代码:
std::string nameString("Alex"); Creature c(nameString);
nameString
如果我在调用时通过引用传递m_name{name},如果我在传递它时通过值传递(然后它被移动)。我知道这比按值传递而不是使用要好std::move(因为它被复制了两次)。
m_name{name}
所以两个问题:
我是否正确理解了这里发生的事情?
是的。
std::move使用通过引用传递并只是调用有什么好处m_name{name}吗?
一个易于掌握的函数签名,没有任何额外的重载。签名立即显示该参数将被复制 - 这使调用者不必怀疑const std::string&引用是否可能存储为数据成员,以后可能会成为悬空引用。当右值传递给函数时,不需要重载std::string&& name和参数以避免不必要的副本。const std::string&传递左值
const std::string&
std::string&& name
通过值获取参数的函数会导致一个副本和一个移动构造。将右值传递给同一个函数
std::string nameString("Alex"); Creature c(std::move(nameString));
导致两个移动结构。相反,当函数参数为const std::string&时,即使传递右值参数,也总会有一个副本。这显然是一个优势,只要参数类型对移动构造便宜(这是 的情况std::string)。
std::string
但是有一个缺点需要考虑:推理不适用于将函数参数分配给另一个变量(而不是初始化它)的函数:
void setName(std::string name) { m_name = std::move(name); }