在C ++ 11中传递值是合理的默认值?

在传统的C ++中,将值传递给函数和方法对于大对象来说是缓慢的,并且通常是不被接受的。 相反,C ++程序员倾向于传递引用,这是更快的,但是引入围绕所有权的各种复杂的问题,尤其是围绕内存pipe理(如果对象是堆分配的话)

现在,在C ++ 11中,我们有Rvalue引用和移动构造函数,这意味着可以实现一个很大的对象(比如std::vector ),这个对象可以很便宜地传入和传出函数。

那么,这是否意味着默认值应该是为types的实例传递值,比如std::vectorstd::string ? 那么自定义对象呢? 什么是新的最佳做法?

这是一个合理的默认, 如果你需要在体内复制。 这就是戴夫·亚伯拉罕( Dave Abrahams) 所倡导的 :

指南:不要复制你的函数参数。 相反,按值传递它们,并让编译器进行复制。

在代码中,这意味着不要这样做:

 void foo(T const& t) { auto copy = t; // ... } 

但是这样做:

 void foo(T t) { // ... } 

其优点是调用者可以像这样使用foo

 T lval; foo(lval); // copy from lvalue foo(T {}); // (potential) move from prvalue foo(std::move(lval)); // (potential) move from xvalue 

只做最less的工作。 你需要两个重载来做同样的引用, void foo(T const&);void foo(T&&);

考虑到这一点,我现在写下了我的有价值的构造函数:

 class T { U u; V v; public: T(U u, V v) : u(std::move(u)) , v(std::move(v)) {} }; 

否则,通过引用const传递仍然是合理的。

在几乎所有的情况下,你的语义应该是:

 bar(foo f); // want to obtain a copy of f bar(const foo& f); // want to read f bar(foo& f); // want to modify f 

所有其他签名只能使用,并有充分的理由。 编译器现在几乎总是以最有效的方式解决这些问题。 你可以继续写你的代码!

如果在函数体内部需要一个对象的副本,或者只需要移动对象,则按值传递参数。 通过const&传递,如果你只需要对对象的非变异访问。

对象复制示例:

 void copy_antipattern(T const& t) { // (Don't do this.) auto copy = t; t.some_mutating_function(); } void copy_pattern(T t) { // (Do this instead.) t.some_mutating_function(); } 

对象移动示例:

 std::vector<T> v; void move_antipattern(T const& t) { v.push_back(t); } void move_pattern(T t) { v.push_back(std::move(t)); } 

非变异访问示例:

 void read_pattern(T const& t) { t.some_const_function(); } 

有关理由,请参阅Dave Abrahams和Xiang Fan的这些博客文章。

函数参数的合理默认值,按使用可能性顺序:

 // Read-only or take a copy of f. void bar(const Foo& f); // Want to modify f. void bar(Foo& f); // Take a copy of f in the most efficient way possible, provide 2 methods: void bar(const Foo& f); // 1 copy, that possibly reuses memory from the original. void bar(Foo&& f); // 1 move. 

通过价值传递副本的效率比上面的build议低。

 void bar(Foo f) // Take a copy of f. { Foo copy = std::move(f); ... } // Client passes an lvalue. 1 copy + 1 move. bar(f); // Bad, because of the extra move, and the quality of the copy // is worse in some cases. When copying from a const ref // parameter, a copy may reuse some allocated memory in the // existing object (say for large strings or vectors). The // copy from a passed value pays the full cost up front. // Client passes an rvalue. 2 moves. bar(std::move(f)); // 2 moves is likely a win over 1 copy, but if you're // interested in optimizing rvalues then providing an // rvalue function mentioned in the advice above // will only have 1 move. 

请注意,类构造函数应该违反这个build议并按值传递参数,因为当存在多个参数时,它会阻止const ref / rvalue产品的组合式爆炸,并且由于不存在任何值,所以它必须支付全部的副本成本。

对于想要了解更多信息的发烧友,请参阅Herb Sutter 2014 CppCon讲座: https : //www.youtube.com/watch?v = xnqTKD8uD64