C++完美转发

1. std::forawrd

std::forward<T>(arg) 可以实现完美转发,即如果 arg 是一个右值引用,则转发之后结果仍是右值引用;反之,如果 arg 是一个左值引用,则转发之后结果仍是左值引用.

#include <iostream>

struct BigObject
{
	char data[1 << 10];
};

void g(BigObject& o)
{
	std::cout << "lvalue reference\n";
}

void g(BigObject&& o)
{
	std::cout << "rvalue reference\n";
}

template <typename T>
void f(T&& arg)
{
	g(std::forward<T>(arg));
}


int main()
{
	BigObject o;
	f(o);

	f(BigObject());
}
lvalue reference
rvalue reference

2. 为什么需要完美转发

  • 在函数模板编程中,常有一种场景是使用模板参数去调用另一个函数(如,上例中 f 去调用 g),这时候如果只提供值传递版本会显得效率太低。
  • 函数的参数一般会尽可能地设为引用类型,以避免对象拷贝带来的高昂开销.
  • 为了使一个函数既可以接受左值,又可以接受右值,C++11 之前的解决方案是将参数类型设为 const Type&. 但这并不是很方便,如限制了参数是常量.
  • 如果函数 g 既提供了左值引用版本和右值引用版本,则最好的情况是函数 f 可以根据参数类型去调用相应版本的 g. 而完美转发正可以满足此要求.

3. 引用折叠规则

  • 右值引用和右值引用叠加将得到右值引用;
  • 右值引用和左值引用叠加将得到左值引用;
  • 左值引用和左值引用叠加将得到左值引用.
template <typename T>
using TR = T&;
						   // v 的类型
TR<int> v;				 // int&
TR<int>& v;				// int&
TR<int>&& v;			   // int&
template <typename T>
using TRR = T&&;
						    // v 的类型
TRR<int> v;				 // int&&
TRR<int>& v;				// int&
TRR<int>&& v;			   // int&&

4. 完美转发的原理

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;

std::remove_reference<T> 的作用就是去掉 T 中的引用,它是通过模板特化来实现:

template< class T > struct remove_reference      {typedef T type;};
template< class T > struct remove_reference<T&>  {typedef T type;};
template< class T > struct remove_reference<T&&> {typedef T type;};

根据上述引用折叠规则,如果 Tint&,则 T&& 即为 int&;反之,如果 Tint&&,则 T&&int&&.

5. forwarding reference

上例函数 f 中的 T&& 实际上被称为 forwarding reference. 它是一种特殊类型的引用,它保留了函数参数的值类别(category),使得可以通过 std::forward 来转发它.

forwarding reference 包括以下两种:

  • 在函数模板中,没有被 constvolatile 修饰的、声明为右值引用的类型形参:

    template<class T>
    int f(T&& x) {                    // x is a forwarding reference
        return g(std::forward<T>(x));
    }
    
    template<class T>
    int g(const T&& x); // x is not a forwarding reference: const T is not cv-unqualified
    
  • auto&&,但如果它跟着一个花括号括起的初始值列表,则它不是 forwarding reference:

    auto&& vec = foo();       // foo() may be lvalue or rvalue, vec is a forwarding reference
    g(std::forward<decltype(vec)>(vec));
    
    auto&& z = {1, 2, 3}; // *not* a forwarding reference (special case for initializer lists)
    
    for (auto&& x: f()) {
    	// x is a forwarding reference; this is the safest way to use range for loops
    }
    
  • 3
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值