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)时非常有用。

引用折叠的规则如下:

  1. lvalue引用与lvalue引用
    • T& & 变为 T&
  2. lvalue引用与rvalue引用
    • T& && 变为 T&
  3. rvalue引用与rvalue引用
    • T&& && 变为 T&&
  4. 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 推导为 intstd::forward 将使其转发为rvalue,调用 process(int&&)

总结

  • 当希望用一个移动操作”窃取“一个对象的表示形式时,使用move;
  • 当希望转发一个对象时,用forward。
  • 因此,forward(x)总是安全的,而move(x)标记x将被销毁,因此要小心使用。
  • 调用move(x)之后x唯一安全的用法就是析构或是赋值的目的。显然,一个特定类型可能提供更多的保证,理想情况下类的不变式保持不变。但是,除非你确切知道这类保证,否则不要依赖他们。
  • 完美转发使得你可以编写更通用和高效的代码。通过结合万能引用和 std::forward,C++11 提供了一种强大且灵活的方式来处理和转发函数参数,而不会丢失其类型信息。

参考

Standard library header utility
使用std::move优化函数返回值

posted @ 2025-02-22 22:25  main_c  阅读(1)  评论(0)    收藏  举报  来源