一、通用引用
- 类型声明形式为
type&&
- 其中
type
类型是要进行推导的,如果类型推导没有发生,那么type&&
代表一个右值引用 - 如果一个对象被声明为
auto&&
,这个形参或者对象就是一个通用引用 - 通用引用,如果它被右值初始化,就会对应地成为右值引用;如果它被左值初始化,就会成为左值引用
- 如果在一个类模板里面看见了一个函数形参类型为
T&&
,也不一定就是通用引用,可能并没有发生类型推导
void 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); //通用引用(不是右值引用)
template <typename T>
void f(const T&& param); //param是一个右值引用,因为添加了 const 限定符
// 函数模板 一般可能是通用引用
template <typename T>
void print_reference_type(T &&i) { // T&& 或是 auto&&
if (std::is_lvalue_reference<decltype(i)>::value) {
std::cout << "lvalue: " << i << std::endl;
} else if (std::is_rvalue_reference<decltype(i)>::value) {
std::cout << "rvalue: " << i << std::endl;
} else {
std::cout << "unknown value: " << i << std::endl;
}
}
// 因为 push_back 在有一个特定的 vector 实例之前不可能存在,
// 而实例化 vector 时的类型已经决定了 push_back 的声明
// 所以在这里并没有发生类型推导
template<class T, class Allocator = allocator<T>> //来自C++标准
class vector
{
public:
void push_back(T&& x);
…
}
一个记录任意函数调用的时间开销的函数模板
auto timeFuncInvocation =
[](auto&& func, auto&&... params) // C++14
// auto&&椒一个通用引用
// params是0个或者多个通用引用
//(即它是个通用引用parameter pack),
//它可以绑定到任意数目、任意类型的对象上
{
start timer;
std::forward<decltype(func)>(func)( //对params调用func
std::forward<delctype(params)>(params)...
);
stop timer and record elapsed time;
};
二、右值引用和移动操作
- 右值引用也是引用,所以和左值引用一样,不能返回局部变量
class B
{
friend std::ostream& operator <<(std::ostream& os, B& b);
public:
B() :s(10), ptr(new int[s])
{
std::cout << "default constructor" << std::endl;
for (int i = 0; i < s; i++)
{
ptr[i] = i;
}
}
B(const B& b) :s(b.s)
{
std::cout << "B&b" << std::endl;
ptr = new int[s];
for (int i = 0; i < s; i++)
ptr[i] = b.ptr[i];
}
B(B&& b) :s(b.s), ptr(b.ptr)
{
std::cout << "B&& b" << std::endl;
b.ptr = nullptr;
}
~B()
{
std::cout << "~B()" << std::endl;
if (ptr)
delete[] ptr;
}
private:
int s;
int* ptr{nullptr};
};
std::ostream& operator <<(std::ostream& os,B& b)
{
os << "[ ";
for (int i = 0; i < b.s; i++)
os << b.ptr[i] << " ";
os << "]" << std::endl;
return os;
}
#if 0
B&& f() // 警告,返回了局部变量的引用
{
B tmp;
return std::move(tmp);
}
B& f2() // 同上
{
B t;
return t;
}
#endif
B f() // 调用移动构造
{
B tmp;
return std::move(tmp);
}
B f2() // 编译器会做优化
{
B t;
return t;
}
int main()
{
B b0 = f2(); // default constructor
// ~B()
// 这里编译器做了优化,减少了对象的拷贝
B b1(f()); // default constructor
// B&& b
// ~B()
// ~B()
// 这里调用了移动构造,所以直接返回局部对象的方式不一定比返回右值引用的方式慢
return 0 ;
}