C++标准库——move、万能引用和forward
@
基于C++11标准
在c++标准库的utility中,提供了下面几个非常用用的小函数:
x2=forward(x):x2是一个右值;不抛出异常x2=move(x):x2是一个右值;不抛出异常x2=move_if_noexcept(x):若x可移动,x2=move(x);否则x2=x;不抛出异常
move
move进行简单的右值转换:
template< class T >
typename std::remove_reference<T>::type&& move( T&& t ) noexcept;
一种可能的实现:
template< class T >
typename std::remove_reference<T>::type&& my_move( T&& t ) noexcept
{
return static_cast<typename std::remove_reference<T>::type&&>(t);
}
我们用move告知编译器:此对象在上下文中不再被使用,因此其值可被移动,留下一个空对象。
最简单的例子是一个swap的可能实现:
template<class T>
void swap(T& a, T& b) noexcept(is_nothrow_move_constructible<T>() && is_nothrow_move_assignable<t>())
{
T tmp{move(a)};
a = move(b);
b = move(tmp);
}
万能引用
万能引用(Universal References)(C++11术语,上述概念更详细描述为“转发引用”):当一个模板参数的类型被声明为T&&时,如果T是一个模板类型参数,那么T&&实际上是一个万能引用,这意味着它可以绑定到lvalue或rvalue。
引用折叠
在C++11中,引用折叠规则(reference collapsing rules)用于处理当多个引用类型结合在一起时所产生的引用类型的推导。这些规则主要针对于模板和类型推导,尤其是在使用转发引用(forwarding references)时非常有用。
引用折叠的规则如下:
- lvalue引用与lvalue引用:
T& &变为T&
- lvalue引用与rvalue引用:
T& &&变为T&
- rvalue引用与rvalue引用:
T&& &&变为T&&
- rvalue引用与lvalue引用:
T&& &变为T&
forward
forward从右值生成一个右值:
template< class T >
T&& forward( typename std::remove_reference<T>::type& t ) noexcept;
template< class T >
T&& forward( typename std::remove_reference<T>::type&& t ) noexcept;
这一对forward函数总是会一起提供,两者之间的选择是通过重载解析实现的。任何左值都会调用第一个版本处理,任何右值都会转向第二个版本处理。
forward的典型用法是将一个实参从一个函数完美转发到另一个函数。标准库make_shared<T>(x)是一个很好的例子。、
通过std::forward<T>(arg),你可以将arg作为保持其值类别的方式传递出去。
完美转发(perfect forwarding)是C++11引入的一个重要特性,旨在确保在将参数传递给其他函数时,能够保留参数的值类别(lvalue/rvalue)和常量性(const/non-const)特性。这在模板编程中尤其有用,能够避免不必要的对象拷贝和转换,提高代码性能和灵活性。
#include <iostream>
#include <utility> // 包含 std::forward
void process(int& x) {
std::cout << "Lvalue: " << x << std::endl;
}
void process(int&& x) {
std::cout << "Rvalue: " << x << std::endl;
}
template <typename T>
void wrapper(T&& arg) {
// 使用 std::forward 进行完美转发
process(std::forward<T>(arg));
}
int main() {
int x = 10;
wrapper(x); // 传递 lvalue
wrapper(20); // 传递 rvalue
}
wrapper是一个模板函数,接受一个万能引用类型的参数T&& arg。- 在
wrapper中使用std::forward<T>(arg)将arg转发到process函数,同时保持其原本的值类别。 - 当
wrapper被调用时,如果传递的是lvalue(如x),则T推导为int&,std::forward将保持这个lvalue特性,相应地调用process(int&)。 - 如果传递的是rvalue(如
20),则T推导为int,std::forward将使其转发为rvalue,调用process(int&&)。
总结
- 当希望用一个移动操作”窃取“一个对象的表示形式时,使用move;
- 当希望转发一个对象时,用forward。
- 因此,forward(x)总是安全的,而move(x)标记x将被销毁,因此要小心使用。
- 调用move(x)之后x唯一安全的用法就是析构或是赋值的目的。显然,一个特定类型可能提供更多的保证,理想情况下类的不变式保持不变。但是,除非你确切知道这类保证,否则不要依赖他们。
- 完美转发使得你可以编写更通用和高效的代码。通过结合万能引用和
std::forward,C++11 提供了一种强大且灵活的方式来处理和转发函数参数,而不会丢失其类型信息。

浙公网安备 33010602011771号