侧边栏壁纸
博主头像
赵东阳的个人网站

行动起来,活在当下

  • 累计撰写 20 篇文章
  • 累计创建 8 个标签
  • 累计收到 1 条评论

目 录CONTENT

文章目录
C++

完美转发

温馨提示:
部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

什么是完美转发?

​ 它指的是函数模板可以将自己的参数“完美”地转发给内部调用的其它函数。所谓完美,即不仅能准确地转发参数的值,还能保证被转发参数的左、右值属性不变。

template<typename T>
void func_A(T t) {
    func_B(t);
}

如果 func_A() 函数接收到的参数 t 为左值,那么该函数传递给 func_B() 的参数 t 也是左值;反之如果 func_A() 函数接收到的参数 t 为右值,那么传递给 func_B() 函数的参数 t 也必须为右值。

在c++98/03中,也可以实现完美转发,但是需要重载两次函数模板

#include <iostream>
using namespace std;

//重载被调用函数,查看完美转发的效果
void func_B(int & t) {
    cout << "lvalue\n";
}
void func_B(const int & t) {
    cout << "rvalue\n";
}

//重载函数模板,分别接收左值和右值
//接收右值参数
template <typename T>
void func_A(const T& t) {
    func_B(t);
}
//接收左值参数
template <typename T>
void func_A(T& t) {
    func_B(t);
}

int main()
{
    func_A(5);//5 是右值
    int  x = 1;
    func_A(x);//x 是左值
    return 0;
}

​ 使用重载的模板函数实现完美转发是有弊端的,仅适用于模板函数仅有少量参数的情况,否则就需要编写大量的重载函数模板,造成代码的冗余。


​ C++11 标准中规定,通常情况下右值引用形式的参数只能接收右值,不能接收左值。

​ 但对于函数模板中使用右值引用语法定义的参数来说,它不再遵守这一规定,既可以接收右值,也可以接收左值(此时的右值引用又被称为“万能引用”)。

template <typename T>
void func_A(T&& t) {  //&&
    func_B(t);
}

这时模板的参数t既可以接受左值也可以接受右值。

C++ 11标准为了更好地实现完美转发,特意为其指定了新的类型匹配规则,又称为引用折叠规则:

int n = 10;

int & num = n;
func_A(num); // T 为 int&

int && num2 = 11;
func_A(num2); // T 为 int &&
  • 当实参为左值或者左值引用(T&)时,函数模板中 T&& 将转变为 T&(T& && = T&);
  • 当实参为右值或者右值引用(T&&)时,函数模板中 T&& 将转变为 T&&(T&& && = T&&)。

这时就可以识别传递进来的参数是左值还是右值了。

模板的问题解决了,那么函数如何接受到这个参数呢?传递进来的参数因为有了名字可以寻址,变为左值了。

我们可以在调用函数的时候也添加&&,如:

template <typename T>
void func_A(T&& t) {
    func_B((T&&)t);
}

为了提高可读性,c++11提供了一个模板函数 forward()

template <typename T>
void func_A(T&& t) {
    func_B(forward<T>(t));
}

fordward(t)如何实现的呢?本质也是强制类型转换,但是C++已经不建议使用C风格的类型转换了。直接在代码中使用static_cast<T&&>(v)也不比用forward(v)简便,后者的可读性还有保障。

template<class _Ty>
_NODISCARD constexpr _Ty&& forward(remove_reference_t<_Ty>& _Arg) noexcept
{	// forward an lvalue as either an lvalue or an rvalue
	return (static_cast<_Ty&&>(_Arg));
}

template<class _Ty>
_NODISCARD constexpr _Ty&& forward(remove_reference_t<_Ty>&& _Arg) noexcept
{	// forward an rvalue as an rvalue
	static_assert(!is_lvalue_reference_v<_Ty>, "bad forward call");
	return (static_cast<_Ty&&>(_Arg));
}

0

评论区