使用 std::move 将参数传递给函数,如果将该参数声明为按值传递或使用移动操作数 &&,是否有区别?
Passing a parameter to a function with std::move, is there a difference if that parameter is declared as pass-by-value or with a move operand &&?
我很好奇这两者之间(对于编译器)是否有任何区别。它们之间唯一的区别是ValueClass在构造函数中通过引用
取参数
#include <string>
class ValueClass
{
public:
ValueClass(std::string obj) : m_obj(std::move(obj)) {}
private:
std::string m_obj;
};
class MoveClass
{
public:
MoveClass(std::string&& obj) : m_obj(std::move(obj)) {}
private:
std::string m_obj;
};
void SomeFunc()
{
std::string a, b;
a = "a";
b = "b";
ValueClass c(std::move(a));
MoveClass d(std::move(b));
}
我快速检查了 goldbolt,确实看起来 MoveClass 构造函数调用导致了更少的汇编行(假设 godbolt 是准确的并且没有错误归因于任何汇编行) .
那么我的问题就变成了,为什么会有差异?编译器是否在将字符串移入并立即移出的地方设置了一些临时存储?
从理论上讲,这是应该优化掉的东西吗?在这种情况下,差异是否纯粹是为了让程序员为函数签名赋予更多意义和信息,以便任何可能想要调用它的人都更容易理解?
ValueClass
需要调用 std::string
的移动构造函数两次:一次创建 obj
参数,然后初始化 m_obj
成员。
MoveClass
只需要调用 std::string
的一个移动构造函数:obj
是对 b
的(右值)引用(没有新的 std::string
对象在这里构造)。只有 m_obj
初始化涉及 std::string
移动构造函数。
是否允许省略任何这些调用(作为假设规则的例外)仍然存在问题。但事实并非如此;成员初始化和函数参数初始化都不是省略构造函数(或临时构造函数)的有效上下文:https://en.cppreference.com/w/cpp/language/copy_elision
现在,在给定的代码中,不一定存在需要编译器保留任何构造函数调用的副作用。事实上,如果您不在 main
中执行重新分配,gcc
会注意到整个程序没有副作用,可以优化为无操作。
您也没有提供 MSVC
正确的参数。最高优化是 /O2
,而不是 -O3
(尽管 -O2
确实有效)。注意编译器警告!
我很好奇这两者之间(对于编译器)是否有任何区别。它们之间唯一的区别是ValueClass在构造函数中通过引用
取参数#include <string>
class ValueClass
{
public:
ValueClass(std::string obj) : m_obj(std::move(obj)) {}
private:
std::string m_obj;
};
class MoveClass
{
public:
MoveClass(std::string&& obj) : m_obj(std::move(obj)) {}
private:
std::string m_obj;
};
void SomeFunc()
{
std::string a, b;
a = "a";
b = "b";
ValueClass c(std::move(a));
MoveClass d(std::move(b));
}
我快速检查了 goldbolt,确实看起来 MoveClass 构造函数调用导致了更少的汇编行(假设 godbolt 是准确的并且没有错误归因于任何汇编行) .
那么我的问题就变成了,为什么会有差异?编译器是否在将字符串移入并立即移出的地方设置了一些临时存储?
从理论上讲,这是应该优化掉的东西吗?在这种情况下,差异是否纯粹是为了让程序员为函数签名赋予更多意义和信息,以便任何可能想要调用它的人都更容易理解?
ValueClass
需要调用 std::string
的移动构造函数两次:一次创建 obj
参数,然后初始化 m_obj
成员。
MoveClass
只需要调用 std::string
的一个移动构造函数:obj
是对 b
的(右值)引用(没有新的 std::string
对象在这里构造)。只有 m_obj
初始化涉及 std::string
移动构造函数。
是否允许省略任何这些调用(作为假设规则的例外)仍然存在问题。但事实并非如此;成员初始化和函数参数初始化都不是省略构造函数(或临时构造函数)的有效上下文:https://en.cppreference.com/w/cpp/language/copy_elision
现在,在给定的代码中,不一定存在需要编译器保留任何构造函数调用的副作用。事实上,如果您不在 main
中执行重新分配,gcc
会注意到整个程序没有副作用,可以优化为无操作。
您也没有提供 MSVC
正确的参数。最高优化是 /O2
,而不是 -O3
(尽管 -O2
确实有效)。注意编译器警告!