我听说你应该总是喜欢C ++ 11中的“按值传递”,因为引入了移动语义。我想看看炒作的全部内容并构建一个测试用例。首先是我的班级:
struct MyClass {
MyClass() { }
MyClass(const MyClass&) { std::cout << "Copy construct" << std::endl; }
MyClass(MyClass&&) { std::cout << "Move construct" << std::endl; }
~MyClass() { }
};
测试工具:
class Test
{
public:
void pass_by_lvalue_ref(const MyClass& myClass)
{
_MyClass.push_back(myClass);
}
void pass_by_rvalue_ref(MyClass&& myClass)
{
_MyClass.push_back(std::move(myClass));
}
void pass_by_value(MyClass myClass)
{
_MyClass.push_back(std::move(myClass));
}
private:
std::vector<MyClass> _MyClass;
};
据推测,pass_by_value
应该优于pass_by_lvalue_ref
和pass_by_rvalue_ref
(一起,而不是单独)。
int main()
{
MyClass myClass;
Test Test;
std::cout << "--lvalue_ref--\n";
Test.pass_by_lvalue_ref(myClass);
std::cout << "--rvalue_ref--\n";
Test.pass_by_rvalue_ref(MyClass{});
std::cout << "--value - lvalue--\n";
Test.pass_by_value(myClass);
std::cout << "--value - rvalue--\n";
Test.pass_by_value(MyClass{});
}
这是我在GCC 4.9.2上的输出-O2
:
--lvalue_ref--
Copy construct
--rvalue_ref--
Move construct
Copy construct
--value - lvalue--
Copy construct
Move construct
Copy construct
Copy construct
--value - rvalue--
Move construct
如您所见,非pass_by_value
函数需要总共2个复制构造和1个移动构造。 pass_by_value
函数总共需要3个复制构造和2个移动构造。看起来,正如预期的那样,对象无论如何都会被复制,那么为什么每个人都说通过值传递?
答案 0 :(得分:2)
首先,您的报告完全有缺陷。每个函数都会推回到同一个向量。当该向量耗尽容量(取决于您目前已插入的项目数)时,它将触发重新分配,这将需要比不触发分配的插入更多的移动和/或副本
其次,std::vector::push_back
有一个strong exception safety guarantee。因此,如果你的移动构造函数不是noexcept,它将不会使用它(除非该类是不可复制的)。它将使用复制构造函数。
第三,
我听说你应该总是喜欢C ++ 11中的“按值传递” 因为引入了移动语义。
我很确定你没有听到任何声誉良好的消息来源。或实际上是不恰当地解释实际所说的内容。但我没有引用的来源。通常建议的是,如果你打算在你的函数中复制你的参数,不要。只需在参数列表中执行(通过按值传递)。这将允许您的函数将r值参数直接移动到其目标。当你传递l值时,它们将被复制,但无论如何你都会这样做。
答案 1 :(得分:0)
如果你打算制作一个内部副本,那么按值传递将完成一个移动构造,而不是一对重载(通过rvalue ref)+(通过const lvalue ref传递)。
如果move构造很便宜,那么这就是少量的运行时开销,以换取更少的编译时间和代码维护开销。
成语是“想要速度?无论如何制作副本?通过值传递,而不是通过const lvalue reference。”实际上。
最后,您的基准测试存在缺陷,因为您在推迟之前未能保留(足够)。重新分配可能会导致额外的操作。哦,并使你的移动构造函数noexcept,因为如果移动可以在很多情况下抛出,那么符合要求的库会更喜欢移动副本。