//c++11中std::move的简化版本
template<typename T>
typename remove_reference<T>::type&& move(T&& param)
//返回类型加上&&表明是一个右值引用
{
// 当T本身是一个左值引用时,T&&就是一个左值引用
// 为了确保&&不会被应用到一个引用类型上
// 需要使用remove_reference来做类型萃取
using ReturnType = typename remove_reference<T>::type&&;
//保证返回值是一个右值引用,该值本身就是右值
return static_cast<ReturnType>(param);
}
//c++14中std::move的简化版本
template<typename T>
decltype(auto) move(T&& param) //返回值类型推导
{
//标准库别名模板
using ReturnType = remove_reference_t<T>&&;
return static_cast<ReturnType>(param);
}
class Annotation
{
public:
//通过拷贝将参数按值传递并设为const防止修改
explicit Annotation(const std::string text)
:value(std::move(text))
// 使用移动操作避免重复拷贝,但是move不改变const属性
//仅仅是将const std::string左值转变成const std::string
//右值,即传递给value构造函数的仍然是const std::string
{
... // construction
}
private:
std::string value;
};
class string
{
public:
...
// const左值引用可以绑定到const 右值
string(const string& rhs);
// 不接受const类型的参数
string(string&& rhs);
};
#include <iostream>
using namespace std;
class A
{
public:
A(const string& a)
{
cout<<"copy construct"<<endl;
cout<<"para:"<<a<<endl;
name_ = a;
cout<<"name:"<<name_<<endl;
}
//此时传进来的rhs是左值变量,但引用的内容是右值
//为了将内容传递给name_,需要将rhs的右值内容通过move来获取
//最终传入string的移动构造函数中
A(string&& rhs):name_(std::move(rhs))
{
cout<<"move construct"<<endl;
cout<<"para:"<<rhs<<endl;
cout<<"name:"<<name_<<endl;
}
private:
string name_;
};
int main() {
A a("blackwall");
//等号左边是左值变量,右边是右值,是内容
string s = "whitewall";
//为了实现移动构造,需要将左值变量的右值内容
//传给移动构造函数的右值引用
A b(std::move(s));
const string ss = "greenwall";
A c(std::move(ss));
cout << "Hello, World!" << endl;
return 0;
}
//output
↪ ./a.out
move construct
para:
name:blackwall
move construct
para:
name:whitewall
copy construct
para:greenwall
name:greenwall
Hello, World!
void process(const Widget& lvalArg);
void process(Widget&& rvalArg);
//外部函数形参接收右值引用
template<typename T>
void logAndProcess(T&& param)
{
auto now = std::chrono::system_clock::now();
makeLogEntry("Calling 'process'", now);
//转发时按照形参接收的类型进行转发
process(std::forward<T>(param));
}
Widget w;
logAndProcess(w); // error, 左值不能绑定到右值上去
logAndProcess(std::move(w));
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(T&& param);
Widget W;
f(w); // 左值传递,左值引用
f(std::move(w)); // 右值传递,右值引用
// 右值引用,因为声明的形式不是T&&,而是std::vector<T>&&
template<typename T>
void f(std::vector<T>&& param);
template<typename T>
void f(const T&& param);
template<class T, class Allocator = allocator<T>>
class vector {
public:
//因为没有特定vector实例存在时,push_back也不会存在
//而实例的类型完全决定了push_back的声明
void push_back(T&& x);
...
};
//当出现如下情况时
std::vector<Widget> v;
//使得
class vector<Widget, allocator<Widget>> {
public:
//右值引用,此处并没有使用类型推导
void push_back(Widget&& x);
...
};
//相反,emplace_back成员函数却使用了类型推导
template<class T, class Allocator = allocator<T>>
class vector {
public:
//此处类型参数Args和vector的类型参数T无关
//所以每次调用时都要做类型推导
template<class... Args>
void emplace_back(Args&&... args);
...
};
class Widget {
public:
template<typename T>
void setName(T&& newName) //通用引用
{ name = std::move(newName); } //但是却使用std::move
...
private:
std::string name;
std::shared_ptr<SomeDataStructure> p;
};
std::string getWidgetName(); //工厂函数
Widget w;
auto n = getWidgetName(); //n是一个局部变量
//把n的值给移动走了,因为通用引用可以识别左值或右值引用
w.setName(n);
template<typename T>
void setSignText(T&& text)
{
sign.setText(text); // 使用text但不修改它
auto now = std::chrono::system_clock::now();
//有条件地将text转换成右值
signHistory.add(now, std::forward<T>(text));
}
Matrix operator+(Matrix&& lhs, const Matrix& rhs)
{
lhs+=rhs;
//移动lhs到返回值内存中,即便Matrix不支持移动
//也只会简单的把右值拷贝到返回值内存中
return std::move(lhs);
}
Matrix operator+(Matrix&& lhs, const Matrix& rhs)
{
lhs+=rhs;
return lhs; //拷贝lhs到返回值内存中
}
template<typename T>
Fraction reduceAndCopy(T&& frac)
{
frac.reduce();
//如果传入的是右值,就移动返回,如果是左值,就拷贝返回
return std::forward<T>(frac);
}
Widget makeWidget()
{
Widget w;
...
return w; //“拷贝”返回
}
Widget makeWidget()
{
Widget w;
...
return std::move(w); // “移动”返回
}
Widget makeWidget(Widget w)
{
...
return w; ---> return std::move(w);
//被编译器认为是一个右值
}
std::multiset<std::string> names; //全局数据结构
std::string nameFromIdx(int idx); //根据索引返回姓名字符串
template<typename T>
void logAndAdd(T&& name)
{
auto now = std::chrono::system_clock::now();
log(now, "logAndAdd");
names.emplace(std::forward<T>(name));
}
void logAndAdd(int idx)
{
auto now = std::chrono::system_clock::now();
log(now, "logAndAdd");
names.emplace(nameFromIdx(idx));
}
std::string petName("Darla");
logAndAdd(petName); //拷贝左值
logAndAdd(std::string("Persephone")); //移动右值
//直接在multiset中创建string而不是拷贝一个临时字符串
logAndAdd("Patty Dog");
short nameIdx = 22;
//错误,short参数将会匹配到通用引用参数的函数调用
//在将short参数转发到names的string构造函数中时,会出错
logAndAdd(nameIdx);
class Person{
public:
template<typename T>
explicit Person(T&& n):name(std::forward<T>(n)) {}
explicit Person(int idx):name(nameFromIdx)) {}
Person(const Person& rhs); //编译器自动产生
Person(Person&& rhs); //编译器自动产生
...
private:
std::string name;
};
Person p("Nancy");
auto cloneOfP(p); //出错!!!
class Person{
public:
explicit Person(std::string n):name(std::move(n)) {}
explicit Person(int idx):name(nameFromIdx)) {}
...
private:
std::string name;
};
//标签分发函数,通过使用对参数类型的判断
//使得通用引用参数获得的匹配无效
//将控制流分发到两个不同的处理函数中
template<typename T>
void logAndAdd(T&& name)
{
//此处必须去掉引用,因为std::is_integral会把int& 判断为
//非int类型,也就是std::false_type
logAndAddImpl(
std::forward<T>(name),
std::is_integral<typename std::remove_reference<T>::type>()
);
}
//处理非整型参数
template<typename T>
void logAndAddImpl(T&& name, std::false_type)
{
auto now = std::chrono::system_clock::now();
log(now, "logAndAdd");
names.emplace(std::forward<T>(name));
}
//处理整型参数类型
std::string nameFromIdx(int idx);
void logAndAddImpl(int idx, std::true_type)
{
//将整型转换成字符串,再重新转发到标签分发函数中,再次分发
logAndAdd(nameFromIdx(idx));
}
class Person{
public:
//在condition中指定满足什么条件
template<typename T,
typename = typename std::enable_if<condition>::type>
explicit Person(T&& n);
...
};
class Person{
public:
template<typename T,
typename = typename std::enable_if<!std::is_same<Person, T>::value>::type>
explicit Person(T&& n);
...
};
//无论是否是引用: Person , Person& , Person&&应该和Person一样
//无论是否是const或volatile: const Person , volatile Person
//const volatile Person应该和Person一样
class Person{
public:
template<typename T, typename = typename std::enable_if<!std::is_same<Person, typename std::decay<T>::type>::value>::type>
explicit Person(T&& n);
...
};
class SpecialPerson: public Person{
public:
//子类构造函数应该先调用父类构造函数,但是传入的参数类型是
//SpecialPerson,根据上面的类型判断,Person与SpecialPerson
//不是同一个类型,因此会禁用模板函数,转而调用拷贝构造函数
SpecialPerson(const SpecialPerson& rhs):
Person(rhs) {...}
SpecialPerson(SpecialPerson&& rhs):
Person(std::move(rhs)) {...}
...
};
class Person{
public:
template<typename T,
typename = typename std::enable_if<
!std::is_base_of<
Person, typename std::decay<T>::type
>::value
>::type
>
explicit Person(T&& n);
...
};
class Person{
public:
template<typename T, typename = typename std::enable_if<!std::is_base_of<Person, typename std::decay<T>::type>::value && !std::is_integral<std::remove_reference<T>::type>()>::type>
explicit Person(T&& n): name(std::forward<T>(n)) {...}
explicit Person(int idx): name(nameFromIdx(idx)) {...}
...
private:
std::string name;
};
class Person{
public:
template<typename T, typename = typename std::enable_if<!std::is_base_of<Person, typename std::decay<T>::type>::value && !std::is_integral<std::remove_reference<T>::type>()>::type>
explicit Person(T&& n): name(std::forward<T>(n))
{
//因为该函数在转发之后执行
//因此这条错误信息将会在左右错误信息输出之后出现
static_assert(std::is_constructible<std::string, T>::value, "Parameter n can't be used to construct a std::string");
}
...
private:
std::string name;
};
template<typename T>
void func(T&& param);
Widget widgetFactory();
Widget w;
func(w); // T被推导为Widget&
func(widgetFactory()); //T被推导为Widget
int x;
...
auto& & rx = x; // 错误
template<typename T>
void func(T&& param);
Widget w;
func(w); // T被推导为Widget&
template<typename T>
T&& forward(typename remove_reference<T>::type& param)
{
return static_cast<T&&>(param);
}
template<typename T>
void f(T&& fParam)
{
...
someFunc(std::forward<T>(fParam);
}
1.传入左值
Widget w;
f(w);
------->
void f(Widget& fParam)
{
...
someFunc(std::forward<Widget&>(fParam);
}
Widget& && forward(typename remove_reference<Widget&>::type& param)
{
return static_cast<Widget& &&>(param);
}
--->
Widget& && forward(Widget& param)
{
return static_cast<Widget& &&>(param);
}
->
Widget& forward(Widget& param)
{
return static_cast<Widget&>(param);
}
2.传入右值
Widget w;
f(std::move(w));
------->
void f(Widget fParam)
{
...
someFunc(std::forward<Widget>(fParam);
}
Widget && forward(typename remove_reference<Widget>::type& param)
{
return static_cast<Widget&&>(param);
}
--->
Widget&& forward(Widget& param)
{
return static_cast<Widget&&>(param);
}
Widget widgetFactory();
Widget w;
//发生了引用折叠
auto&& w1 = w; --> Widget& && w1 = w; --> Widget& w1 = w;
//没有发生引用折叠
auto&& w2 = widgetFactory(); -->Widget&& w2 = widgetFactory();
template<typename T>
class Widget{
public:
typedef T&& RvalueRefToT;
...
};
Widget<int&> w; --> typedef int& && RvalueRefToT; --> typedef int& RvalueRefToT;
template<typename T>
void fwd(T&& param)
{
f(std::forward<T>(param));
}
template<typename... Ts>
void fwd(Ts&&... params)
{
f(std::forward<Ts>(params)...);
}
void f(const std::vector<int>& v);
f({1,2,3}); //会被隐式转换成std::vector<int>
fwd({1,2,3}); //无法编译
auto il = {1,2,3};
fwd(il);
class Widget {
public:
static constexpr std::size_t MinVals = 28;
...
};
...
std::vector<int> widgetData;
widgetData.reserve(Widget::MinVals); //没问题
void f(std::size_t val);
f(Widget::MinVals); --> f(28); //没问题
fwd(Widget::MinVals); //出错
constexpr std::size_t Widget::MinVals;
template<typename T>
void fwd(T&& param)
{
f(std::forward<T>(param));
}
void f(int (*pf)(int)); / void f(int pf(int));
int processVal(int value);
int processVal(int value, int priority);
//虽然processVal不是一个类型
//但编译器可以正确匹配到第一个重载函数
f(processVal);
//错误,proecssVal不是一个类型
//自动推导的fwd不知道该匹配哪一个重载函数
fwd(processVal);
template<typename T>
T workOnVal(T param) {...}
//出错,不知道匹配哪一个模板函数实例
fwd(workOnVal);
using ProcessFuncType = int (*)(int);
ProcessFuncType processValPtr = processVal; //指定函数
fwd(processValPtr); //可以正确转发,因为类型已经指定
//可以正确转发,因为已经实例化
fwd(static_cast<ProcessFuncType>(workOnVal);
struct IPv4Header {
std::uint32_t version:4, IHL:4, DSCP:6, ECN:2, totalLength:16;
...
};
void f(std::size_t sz);
IPv4Header h;
//没问题,bit会自动转换成size_t
f(h.totalLength);
//出错,因为fwd的参数是一个通用引用
//而C++标准规定:非const类型的引用不能绑定到bit域上
//因为没有办法寻址
fwd(h.totalLength);
//bit域参数传递的可行方式只有:按值传递,或者加上const修饰的引用。
//按值传递时,函数会接收到bit域里面的值
//按const引用传递时,会首先将bit域的值拷贝到一个整型类型中,
//然后再绑定到该类型上
auto length = static_cast<std::uint16_t>(h.totalLength);
fwd(length);
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。