C++:引用,万能引用,引用折叠,std::forward一次带你搞明白

C++:引用,万能引用,引用折叠,std::forward一次带你搞明白,第1张

前言胡扯

 

我上一篇博客讲了move,跟这篇博客有很强的关联性,这篇博客我也会提到很多上篇博客讲的东西,希望大家看到感兴趣想多了解的话,可以去看看我的往期博客,会有所收获的。

一,万能引用

        万能引用只有一种形式:T&&,不能携带任何的修饰符。

        我上篇博客提到了一句很重要的话:函数的形参永远是左值。

        

#include
#include
template
void fun(T&& t)
{
	std::cout << std::boolalpha;
	std::cout << std::is_reference_v << std::endl;
	std::cout << std::is_const_v << std::endl;
}
int main()
{
	const int a = 5;
	fun(5);
	fun(a);//T 是 const int&
}

 

        我们能得到第二个是引用,第一个不是引用,第二个是左值,第一个不是左值 。

        我们看上面的例子才能知道通过万能引用推导规则,知道什么时候是右值,什么时候是左值:

        左值————>左值引用(加上引用):看到带引用的就知道它原来是左值

        右值————>不带引用(把引用去掉) :看到不带引用的就知道它原来是右值

        我们发现我们传进去的const的修饰符也消失了,但实际上并不是如此,这里就要有顶层const和底层const的区别了,这里我不过多介绍,可以看我往期博客有专门介绍const,引用是只有底层const的,不存在顶层const你只能const int&这样写引用对吧,而is_const_v这个方法它只能检测到顶层const,所以输出了false。以上就是万能引用的推导规则。

二,引用折叠

        引用折叠只能应用于推导的语境下。(如:模板实例化,auto,decltype等)

        右值引用加上右值引用等于右值引用(&&+&&==&&),除了这个外其他的所有形式都为左值引用。

        记住这两句话,你就可以解决引用折叠的百分之一百的问题了我们接着来看这个例子

        

#include
#include
template
void fun(T&& t)
{
	std::cout << std::boolalpha;
	std::cout << std::is_reference_v << std::endl;
	std::cout << std::is_const_v << std::endl;
}
int main()
{
	const int a = 5;
	fun(5);//
	fun(a);//t=const int& + &&==const int&
}

         所以能看出大概是这么个情况,所以推导为了左值引用。

三,std::forward

        forward不进行任何转发,是在某个特定情况下,进行一个强制转换 (static_cast)

        而我上篇博客讲的move 是强制转换为右值引用,看清楚forward和move的区别。当我们有一个复杂的对象时,我们更希望可以移动它而不是拷贝它浪费性能,所以我们希望它们变成右值,就得判断传入的形参为右值还是左值,通过万能引用的推导规则我能知道,左值为左值引用,右值不带引用,这样就能知道该调用移动语句还是复制语句,forward就是基于上面实现的,我们拿上一篇的博客举例。

#include
#include
#include
class Vector
{
private:
	int x, y, z;
public:
	Vector(int x, int y, int z) :x(x), y(y), z(z) {}
};
std::vector vec;
template
void fun(T&& t)
{
	vec.push_back(std::forward(t));
}
int main()
{
	const Vector v0(1, 2, 3);
	fun(v0);
	fun(Vector(2, 3, 4));
}

他们分别会调用

    _CONSTEXPR20_CONTAINER void push_back(const _Ty& _Val) { // insert element at end, provide strong guarantee
        emplace_back(_Val);
    }

    _CONSTEXPR20_CONTAINER void push_back(_Ty&& _Val) {
        // insert by moving into element at end, provide strong guarantee
        emplace_back(_STD move(_Val));
    }

左右值会分别调用上面的复制语句和移动语义,我们最后看一下forward的源码吧

template 
_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 
_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);
}

我们看到了他是个重载函数,通过万能引用和引用折叠,返还出我们的值,我们传入的值无非就是,带引用和不带引用两种,第二个它加了个断言用于不加引用的版本以防出错。我们看到了std::forward很简单,当传入参数为不是引用或为右值引用我就强转为右值引用,其他强转为左值引用。

结语

万能引用,引用折叠和完美转发到此也就讲完了,如果觉得会有所收获的话,可以看看博主的往期博客一定会有所收获顺便关注下博主,当然以上内容哪里出错了的话,还是麻烦大家多来指正,好的谢谢大家看到这里!!!!

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/867308.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-12
下一篇 2022-05-12

发表评论

登录后才能评论

评论列表(0条)

保存