c++ - 值传递和 std::move 优于传递引用的优点
问题描述
我目前正在学习 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
所以我应该删除它。
我没有收到警告的唯一方法是const
完全删除:
Creature(std::string name)
: m_name{std::move(name)}
{
}
这似乎是合乎逻辑的,因为唯一的好处const
是防止弄乱原始字符串(这不会发生,因为我按值传递)。但我在CPlusPlus.com上读到:
尽管请注意 - 在标准库中 - 移动意味着被移动的对象处于有效但未指定的状态。这意味着,在这样的操作之后,被移动对象的值应该只被销毁或分配一个新值;否则访问它会产生一个未指定的值。
现在想象一下这段代码:
std::string nameString("Alex");
Creature c(nameString);
因为nameString
按值传递,std::move
只会name
在构造函数内部无效,不会触及原始字符串。但是这样做有什么好处呢?无论如何,内容似乎只被复制一次 - 如果我在调用时通过引用传递m_name{name}
,如果我在传递它时通过值传递(然后它被移动)。我知道这比按值传递而不是使用要好std::move
(因为它被复制了两次)。
所以两个问题:
- 我是否正确理解了这里发生的事情?
std::move
使用通过引用传递并只是调用有什么好处m_name{name}
吗?
解决方案
/* (0) */
Creature(const std::string &name) : m_name{name} { }
传递的左值绑定到
name
,然后复制到m_name
。传递的右值绑定到
name
,然后复制到m_name
。
/* (1) */
Creature(std::string name) : m_name{std::move(name)} { }
传递的左值被复制到
name
中,然后被移动到m_name
中。传递的右值被移入,
name
然后被移入m_name
。
/* (2) */
Creature(const std::string &name) : m_name{name} { }
Creature(std::string &&rname) : m_name{std::move(rname)} { }
传递的左值绑定到
name
,然后复制到m_name
。传递的右值绑定到
rname
,然后移动到m_name
。
由于移动操作通常比副本快,如果您通过大量临时对象, (1)优于(0) 。(2)在复制/移动方面是最佳的,但需要代码重复。
完美转发可以避免代码重复:
/* (3) */
template <typename T,
std::enable_if_t<
std::is_convertible_v<std::remove_cvref_t<T>, std::string>,
int> = 0
>
Creature(T&& name) : m_name{std::forward<T>(name)} { }
您可能希望限制T
以限制可以实例化此构造函数的类型域(如上所示)。C++20 旨在通过概念简化这一点。
在 C++17 中,prvalues受保证复制省略的影响,当适用时,这将减少向函数传递参数时的复制/移动次数。
推荐阅读
- python - seaborn中的奇怪事情以及如何纠正它
- javascript - 使用对象解构来 require() Express Router
- javascript - 是否可以创建一个在 js 中创建特定 Dom 元素的模块?
- c - 递归函数来比较没有库函数的字符串
- linux - 分段错误 ARM cross complie Linux arm920t
- python - 如何使用我的 LSTM 输出作为 keras 中另一个 LSTM 的输入?
- c# - 在 C# 中使用 SQL
- python - 绘制来自调查的分组信息
- c++ - 如何在 ncurses c++ 中从屏幕获取文本?
- javascript - 调用 setState 后的事件给了我以前的状态值