对容器的 insertion 操作,比如
push_back
,有的时候效率并不高。
Effective C++ :如果拷贝和移动的负担很小,那么考虑使用拷贝
为了兼顾性能和功能性,有些类会提供拷贝和移动两个版本:
1
2
3
4
5
6
7
8
9class Widget {
public:
void addName(const std::string& newName) // take lvalue;
{ names.push_back(newName); } // copy it
void addName(std::string&& newName) // take rvalue;
{ names.push_back(std::move(newName)); } // move it;
private:
std::vector<std::string> names;
};
使用完美转发来实现一个简易版本: 1
2
3
4
5
6
7
8class Widget {
public:
template<typename T> // take lvalues
void addName(T&& newName) // and rvalues;
{ // copy lvalues,
names.push_back(std::forward<T>(newName)); // move rvalues;
}
};
但通用引用可以接收各种类型的参数,这在传入错误的参数时,编译器的报错信息会异常繁琐。其实还有更好的选择。
Effective C++ :熟悉完美转发的错误情况
要认识到完美转发的一些错误用例,才能够很好的使用它。
Effective C++ :正确使用 lambda
lambda 使得 c++ 更有魅力,使用 lambda
可以创建一个可调用的对象,一般用于以下场合: 1.
快速创建一个简短的函数,仅在父函数中使用,避免繁琐的创建成员函数的过程。
2. 为标准库的算法(std::find_if,std::sort
等),优雅的创建一个可调用对象。 3.
快速为标准库创建删除器,比如std::unique_ptr,std::shared_ptr
等
Effective C++ :谨慎使用移动操作
移动语义是 c++11 最重要的特性,但不是什么情况下都可以使用移动语义。 所以写代码的时候不能假设就有移动语义而忽视性能方面应该注意的问题。
比如只有当一个类没有显示的定义拷贝、移动、析构函数时,编译器才会生成一个默认的移动构造、拷贝函数。
或者是一个类的成员变量类禁止了移动操作,那么也不会生成默认移动函数。
Effective C++ :理解引用折叠
如果一个模板函数的形参是通用引用,那么: 1.
如果传入的实参是左值,那么推导出来的形参就是左值引用 2.
如果传入的实参是右值,那么推导出来的形参就是不带引用的普通形参
1
2
3
4
5
6Widget widgetFactory(); // function returning rvalue
Widget w; // a variable (an lvalue)
func(w); // call func with lvalue; T deduced
// to be Widget&
func(widgetFactory()); // call func with rvalue; T deduced
// to be Widget
Effective C++ :以自己熟悉的方式代替重载通用引用的方式
前面提到过,重载通用引用函数,得到的结果往往不是预期的,那么就应该以其它的方式来替代这种迷惑的方式。
Effective C++ :不要创建通用引用的重载函数
创建通用引用的重载函数,但很多时候还是会调用到通用引用函数,这会让人很迷惑。
Effective C++ :区分通用引用和右值引用
声明一个右值引用,使用T&&
这种格式,但是这玩意并不是表面上看到的那么简单。
1
2
3
4
5
6
7void f(Widget&& param); // 右值引用
Widget&& var1 = Widget(); // 右值引用
auto&& var2 = var1; // 通用引用
template<typename T>
void f(std::vector<T>&& param); // 右值引用
template<typename T>
void f(T&& param); // 通用引用
Effective C++ :对右值引用使用 std::move,对通用引用使用 std::forward
当形参被声明为右值引用时,意味着传入的实参需要是右值引用,并且该参数是可移动的。既然目的如此明确,那么使用std::move
是正确的选择:
1
2
3
4
5
6
7
8
9
10
11class Widget {
public:
Widget(Widget&& rhs) // rhs is rvalue reference
: name(std::move(rhs.name)),
p(std::move(rhs.p))
{ … }
…
private:
std::string name;
std::shared_ptr<SomeDataStructure> p;
};std::forward
是正确的选择:
1
2
3
4
5
6
7class Widget {
public:
template<typename T>
void setName(T&& newName) // newName is
{ name = std::forward<T>(newName); } // universal reference
…
};