模板参数的右值引用形参
template <typename T>
void f5(T&&);
当函数是一个模板函数的时候,当他的形参是右值引用的时候有如下的不同:
传入左值,传入值的类型被推断为类型的引用
比如,传入string类型,那么T被推断为string&
同时,引用折叠:怎么说,意思就是左值引用的引用就等于引用,也是是,所有的双数引用可以折叠为右值引用。
而所有单数的引用可以折叠为左值引用
T&& && &&=T&&
T&& && && & =T&
因此,综上棉量两点:
当T = string &&是,传入的参数类型为string& &&,因此被折叠为string &。
这就是模板函数的右值引用形参可以接受一个左值的原因。
普通函数的右值引用形参
stl大量的使用了
type_traits
技术来提取参数类型
例如std::move()
template <class _Ty>
inline constexpr typename remove_reference<_Ty>::type &&
move(_Ty &&_Arg) _NOEXCEPT
{ // forward _Arg as movable
return (static_cast<typename remove_reference<_Ty>::type &&>(_Arg));
}
而remove_reference<_Ty>
template <class _Ty>
struct remove_reference
{ // remove reference
typedef _Ty type;
};
template <class _Ty>
struct remove_reference<_Ty &>
{ // remove reference
typedef _Ty type;
};
template <class _Ty>
struct remove_reference<_Ty &&>
{ // remove rvalue reference
typedef _Ty type;
};
因此remove_reference
能够根据传入参数的不同,在其typedef _Ty type
中萃取出传入的类型。
而std::move
中的static_cast<typename remove_reference<_Ty>::type &&>(_Arg)
进行类型转换。
步骤是:根据传入的参数,萃取出参数的类型,然后转化为参数的右值引用类型。
编译器行为
现在的编译器基本上都会做返回值优化(return value optimization)。也就是说,编译器会在函数返回的地方直接创建对象,而不是在函数中创建后再复制出来。很明显,这比move语义还要好一点。
这也就是为什么有的时候,栈参数的地址比函数内对象的地址还要低。因为要返回的对象被直接创建在返回值处。