1 右值引用
2 move 语义
3 forward 和 完美转发
4 emplace_back: 右值版本的插入函数
比 push_back 减少 内存 拷贝 和 移动
5 无序容器 unordered_...
插入元素时 不会自动排序
在 不需要排序时, 不会带来额外的性能损耗
2.1 右值引用
1 左值 / 右值 / 右值引用 / 常量左值引用 / universal reference
(1) 左值: 表达式结束后 依然存在的持久对象
(2) 右值: 表达式结束后 不再存在的临时对象
如何区分 ?
不能取地址 => 右值
无名 => 右值
2 种右值
纯右值(prvalue, pure rvalue)
non-ref 返回 的 临时变量
运算表达式产生 的 临时变量
原始字面量
lambda 表达式
将亡值(xvalue, expiring value) // C++11 新增
将被移动 的 管理对象
std::move 返回值
T&& 函数返回值
转换为 T&& 类型的 转换函数的返回值
(3) 右值引用: A&& // A 不经编译器类型推导
是 1 种类型
将 右值 的 lifetime 延长到 与 右值引用(类型的)变量 的 lifetime 一样长
1) 作用/应用
[1] 减少了 新对象 a 的
1] copy ctor: from 函数返回的临时对象 getA()
2] dtor
[2] 支持 move 语义
RAII 类
前提: 类 有 move 语义的 memFunc
1] 隐含 move: 一侧为 右值
|
| 对 左值 move
|/
2] 显式 move: std::move()
// === 右值引用应用1: 2 行代码:
A a = getA();
3 steps
1] 临时变量 getA() 到 a 的 copy
2] getA() 的 dtor
3] a 的 dtor: a scope 结束时
A&& a = getA();
1] 右值引用 绑定
2] 被延长 lifetime 的 临时对象 getA() 的 dtor
struct A
{
A() {}
A(const A& rhs) { }
~A() { }
};
A getA()
{
return A();
}
3 step3:
1] 临时变量 A() ctor
2] 临时变量 A() 到 临时变量 getA() 的 copy ctor
3] A() dtor
gcc 设 编译选项 -fno-elide-constructors 关闭返回值优化效果
(4) 常量左值引用: const A&
可接收 左值/右值/常量左值/常量右值
也可以实现 右值引用 作用1 的效果
const A& a = getA();
(5) universal reference 类型: 未定的引用类型 T&&/auto&&
发生 自动类型推断(函数模板实参推断 T&& / auto&& 类型推断) 时,
T&& / auto&& t 所绑定变量的 左右值特性未定
绑定的变量可能是 左值或右值
t : 未定的引用
T&& / auto&& : 未定的引用类型
————————————————————————————
t 的
————————————————————————————
[1] 值特性 | 左值或右值
————————————————————————————
[2] 类型 | 未定的引用类型
————————————————————————————
1) universal reference 类型推断
规则: X 类型的 右值 被推断为 X
左值 X&
|
|
看 实参/用于初始化的变量
<=> 引用折叠: 经过类型推导的 T&&/auto&& 相比 右值引用 && 会发生 类型变化
右值引用 + 右值引用 = 右值引用
else, = 左值引用
Note: 引用折叠 的 规则描述 不如 universal reference 类型推断 规则好用好记
与 decltype 推断规则 不同
——————————————————————————————————————————————————————
X x;
t T T&&
f(T&& t)
左值初始化 f(x) X& X& X&
auto t = x; X& X& X&
右值初始化 f( X{} ) X X X&&
auto t = X{}; X X X&&
——————————————————————————————————————————————————————
int&& x = 0;
auto&& y = x; // x 为 int&& 型 左值 => y 是1个/类型为 int& => auto 为 int&
int a, b;
decltype(a)&& r = std::move(b); // not universal reference
2) 像但不是 universal reference 的 case
// 3种 右值引用
template<typename T>
class A
{
public:
A(A&& rhs);
};
void f(std::vector<T>&& vec);
template <typename T>
void f(const T&& t);
2.2 move 语义: 右值引用 应用2
含 ptr 的 class
默认 ctor 是 浅拷贝: 只 copy ptr, 不 copy 所指 memory
A& A::A(const A& rhs);
A& A::operator=(const A& rhs);
问题: 若发生 copy ctor, 则 第2个 obj dtor 时, ptr 重复 delete & 指针悬挂
解决: 深拷贝 copy ctor
问题: copy ctor 可能不必要
临时对象 A() 到 getA(), getA() 到 a 共 2 次 copy ctor 不必要
解决:
move ctor/assignment: 资源 ownership 转交
A& A::A(A&& rhs);
A& A::operator=(A&& rhs);
Note: 提供 move ctor/assignment 的同时, 也提供 copy ctor/assignment, 以 防止 move 不成功时, 还能 copy
2 个例子
(1) 含 ptr 的 class
// 浅 copy
class A
{
private:
int* ptr;
public:
A() : ptr(new int(0)) {}
~A() { delete ptr; }
};
A getA()
{
return A();
}
int main()
{
A a = getA();
}
// 深 copy
#include <iostream>
class A
{
private:
int* ptr;
public:
A() : ptr(new int(0)) {}
A(const A& rhs)
: ptr(new int(*rhs.ptr)) { std::cout << "copy ctor \n"; }
~A() { delete ptr; }
};
A getA()
{
return A();
}
int main()
{
A a = getA();
}
g++ -fno-elide-constructors t.cpp -o a
$ ./a
copy ctor
copy ctor
// ==
#include <iostream>
class A
{
private:
int* ptr;
public:
A() : ptr(new int(0)) {}
A(const A& rhs)
: ptr(new int(*rhs.ptr)) {}
A(A&& rhs) : ptr(rhs.ptr)
{
std::cout << "move ctor \n";
rhs.ptr = nullptr;
}
~A() { delete ptr; }
};
A getA()
{
return A();
}
int main()
{
A a = getA();
}
$ g++ -fno-elide-constructors t.cpp -o a
$ ./a
move ctor
move ctor
(2) 自定义 String
// === copy ctor
#include <iostream>
#include <cstring> // memcpy strlen
class MyString
{
private:
char* ptr;
size_t len;
void copyData(const char* p)
{
len = strlen(p);
ptr = new char[len + 1];
memcpy(ptr, p, len);
ptr[len] = '\0';
}
public:
MyString()
{
ptr = nullptr;
len = 0;
}
MyString(const char* p)
{
copyData(p);
}
MyString(const MyString& rhs)
{
copyData(rhs.ptr);
}
MyString& operator=(const MyString& rhs)
{
if(this != &rhs)
copyData(rhs.ptr);
return *this;
}
~MyString()
{
if (ptr)
delete[] ptr;
}
};
int main()
{
MyString str;
str = MyString("hello");
}
// === move ctor & move assignment
#include <iostream>
#include <cstring> // memcpy strlen
class MyString
{
private:
char* ptr;
size_t len;
void copyData(const char* p)
{
len = strlen(p);
ptr = new char[len + 1];
memcpy(ptr, p, len);
ptr[len] = '\0';
}
public:
MyString()
{
ptr = nullptr;
len = 0;
}
MyString(MyString&& rhs)
{
ptr = rhs.ptr;
len = rhs.len;
rhs.ptr = nullptr;
rhs.len = 0;
}
MyString(const char* p)
{
copyData(p);
}
MyString(const MyString& rhs)
{
copyData(rhs.ptr);
}
MyString& operator=(const MyString& rhs)
{
if(this != &rhs)
copyData(rhs.ptr);
return *this;
}
MyString& operator=(MyString&& rhs)
{
if (this != &rhs)
{
ptr = rhs.ptr;
len = rhs.len;
rhs.ptr = nullptr;
rhs.len = 0;
}
return *this;
}
~MyString()
{
if (ptr)
delete[] ptr;
}
};
int main()
{
MyString str;
str = MyString("hello");
}
2.3 forward & 完美转发 & 万能 function wrapper
1 std::forward <utility>
// version1
template< class T >
T&& forward( typename std::remove_reference<T>::type& t ) noexcept;
// version2
template< class T >
T&& forward( typename std::remove_reference<T>::type&& t ) noexcept;
(1) 引入
func1() 接收 右值, 转发 给 func2() 时 又变为 左值
|
| 解决
|/
std::forward 实现 完美转发
arg lvalue -> t/T&& 被推断为 int& => T = int&, 调 f1(int& t)
-> t 按 lvalue 转发 -> 调 f2(int& t)
arg: rvalue -> t/T&& 被推断为 int&& => T = int, 调 f1(int&& t)
-> t 按 rvalue 转发 -> 调 f2(int&& t)
// === 值 经 1次转发, 右值 变 左值
#include <iostream>
void f2(int& i) { std::cout << "lvalue: \n"; }
void f2(int&& i) { std::cout << "rvalue: \n"; }
template <typename T>
void f1(T&& t)
{
f2(t);
}
int main()
{
f1(0); // lvalue:
}
// === std::forward 实现 完美转发
#include <iostream>
void f2(int& i) { std::cout << "lvalue: \n"; }
void f2(int&& i) { std::cout << "rvalue: \n"; }
template <typename T>
void f1(T&& t)
{
f2(std::forward<T>(t));
}
int main()
{
int x = 0;
f1(x); // lvalue:
f1(0); // rvalue:
}
2 完美转发
保持 参数 t (所绑定值) 的 左右值特性, 将 参数转发 给 函数(模板)中 调用的 another 函数(模板)
以 左/右 值 调 f1, 到 f2 中 仍为 左/右 值
template <typename T>
void f1(T&& t)
{
f2( std::forward<T>(t) );
}
————————————————————————————————————————————————
t 为(绑定到) X 类型的 左值 右值
————————————————————————————————————————————————
=> t/T X& X
————————————————————————————————————————————————
调 std::forward<T>(t)
1] 对 T 去引用 X X
2] 实参匹配形参 version1 version2 of std::forward
3] 返回类型 T&& X& X&&
————————————————————————————————————————————————
3 万能 function wrapper: universal reference + 完美转发 + 可变模板参数
function 无论带不带 return value, 无论带不带 para
都可以委托该 function wrapper 执行
#include <iostream>
#include <utility>
template<typename F, typename... Args>
inline auto funcWrapper(F&& f, Args&& ... args)
-> decltype( f(std::forward<Args>(args)...) )
{
return f(std::forward<Args>(args)...);
}
void test1() { std::cout << "hello\n"; }
int test2(int x) { std::cout << x << std::endl; return x; }
int main()
{
funcWrapper(test1);
funcWrapper(test2, 1);
funcWrapper([](int x) {
std::cout << x << std::endl;
return x;
}, 1);
}
2.4 emplace_back 比 push_back 减少 内存 拷贝 和 移动
1. emplace_back
(1) 机制
就地 通过参数 构造对象 => 无需 copy 或 move 内存
`要求` 容器元素类型 有 相应 Ctor
push_back()
1] 调 Allocator 的 construct() 函数 构造临时对象 (用 指定元素值)
2] 将 临时对象 copy/move(C++11, 才支持 move) 进 容器:
看 优先匹配到 move ctor 还是 copy ctor
(2)
vector<A> vecA;
vecA.emplace_back(args...)
|
|/
args 作 实参 调 A 的 Ctor
2.5 无序容器 unordered_...
————————————————————————————————————————————————————————————————
底层数据结构 通过 what 操作元素
————————————————————————————————————————————————————————————————
set/map 红黑树 排序
————————————————————————————————————————————————————————————————
unordered_ 哈希表 hash => 效率更高
key 需提供 hash_value 函数
自定义 key, 要提供 Hash 函数 和 比较函数
————————————————————————————————————————————————————————————————
网友评论