std::move()详解、左值引用与右值引用区别、C++

很久没有接触C++代码了,很多东西都快忘了,今天刷力扣的题目,发现自己竟然看不懂左右引用转换了,去仔细查了查,写下这篇博客记录。

先上代码

class Solution {
public:
    string simplifyPath(string path) {
        auto split = [](const string& s, char delim) -> vector<string> {
            vector<string> ans;
            string cur;
            for (char ch: s) {
                if (ch == delim) {
                    ans.push_back(move(cur));
                    cur.clear();
                }
                else {
                    cur += ch;
                }
            }
            ans.push_back(move(cur));
            return ans;
        };

        vector<string> names = split(path, '/');
        vector<string> stack;
        for (string& name: names) {
            if (name == "..") {
                if (!stack.empty()) {
                    stack.pop_back();
                }
            }
            else if (!name.empty() && name != ".") {
                stack.push_back(move(name));
            }
        }
        string ans;
        if (stack.empty()) {
            ans = "/";
        }
        else {
            for (string& name: stack) {
                ans += "/" + move(name);
            }
        }
        return ans;
    }
};

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/simplify-path/solution/jian-hua-lu-jing-by-leetcode-solution-aucq/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

这是题解,算法就不说了,一个简单的栈,我们看语法。

vector<string> ans;
string cur;
ans.push_back(move(cur));

先查move():函数的作用:右值引用不能直接绑定到一个左值上,但我们可以用move():获得一个绑定到左值上的右值引用。

左值引用与右值引用

额,有点绕,影响我们阅读的就是不懂左值引用和右值引用,那我们先了解这是什么。

右值引用就是必须绑定到右值的引用,我们只能通过&&而不是&来获得右值引用。–《C++ Primer》

简单理解,左值就是在等号左边的值,右值是在等号右边的值。可以理解为左值表达式表示的是一个对象的身份,而一个右值表达式表示的是对象的值。举例:

int i = 10;//  i为左值,10为右值
int &r = i;//  r是左值引用,绑定到左值上,合法
int &&rr = i;// rr是右值引用,绑定到左值上,不合法
int &r2 = i * 10;//r2是左值引用,绑定到左值上,合法
int &&rr2 = i*10;//rr2是右值引用,绑定到右值上,合法
const int &r3 = i * 10;//合法,这个我也没有很好的理解方法,可以理解为const是常量关键字,表示r3是常量的引用,而右值一般就是常量值。
int &&rr1 = 10;//  字面常量值右值,合法
int &&rr2 = rr1;// rr1是变量,变量是左值,这里将左值绑定到右值变量上,不合法

模板实参推断和引用

到这里任意给一个表达式应该都能判断左值右值了。下面来说函数模板对引用的类型推断。

//从左值引用函数参数推断类型
template<typename T> void f1(T&);//显然实参必须是左值
f1(i); //i是int;则T是int
f1(ci); //ci是const int;则T是const int。举个例子:const int ci = 10;f1(ci);合法,此时T是const int
f1(5); //5不是左值,不合法

//当函数参数是const T&时,我们可以传任何值给此函数
template<typename T> void f2(const T&);//可以接受右值。注意,此时T的类型推断不会是const类型,因为const已经是函数参数类型的一部分,即使推导出T是const,两个const也无意义。在f2中使用参数,此参数一定是const。
f2(i); //i是int,T是int
f2(ci); //i是const int,T是int
f3(10); //T是int,合法,参考前面的左右值举例。

template<typename T> void f3(T&&);
f3(10); //合法,T是int

引用折叠和右值引用参数

这是引用较为重要的一些特性,举例:

f3(i);  //i是int对象

我们用我们已有的知识来判断,这样写应该是错的,因为i是左值,把左值绑定到右值引用上,这是不合法的,但是在时间编写中却可以这样写,因为C++规定了特例。

特例1:将左值传递给函数的右值引用参数,且此右值引用指向模板类型参数(T&&)时,编译器推断模板类型参数为实参的左值引用类型。例如调用f3(i)时,编译器推断T为int&,不是int,可以看做f3的参数是一个int类型的左值引用的右值引用。

特例2:如果我们简介创建了一个引用的引用(如上),则这些引用形成了“折叠”。

·i& &、i& &&、i&& &都折叠成类型i&;

·i&& &&折叠成i&&

注意:引用折叠只能引用于简介创建的引用的引用,如类型别名或模板参数。

由此,当我们调用f3(i);时,f3(i)可能实例化为

void f3<int&>(int& &&);

此时会形成折叠

void f3<int&>(int&);,这样传递i进来也就合法了

如果一个函数参数是一个指向模板类型参数的右值引用(T&&),则他可以绑定一个左值,且如果实参是一个左值,则T将会被推导成一个左值引用,函数参数会被实例化为一个普通的左值引用参数(T&)

总结来说就是我们可以将任意类型的实参传递给T&&类型的函数参数。

template<typename T> void f3(T&& val)
{
    T t = val;
    t = fun(t);
    if (val == t)
    	{
        
    	}
}

示例代码如上,按照两个特例得出的结论:

当传入右值,如10时,T被推导成int,那t被改变后,if里面就应该是false;

当传入左值,如i时,T被推导成int&,此时t就是val的引用,t改变,val也改变,if里面永真。

std::move()

话不多说,先看源码

  template<typename _Tp>
    inline typename std::remove_reference<_Tp>::type&&
    move(_Tp&& __t)
    { 
        return static_cast<typenamestd::remove_reference<_Tp>::type&&>(__t); 
    }

从源码可以看出,move的作用就是返回传入参数的右值引用,不管传入的是左值还是右值,都返回其右值引用。

那现在问题就迎刃而解了,题解源码中的move();就是这个作用,实际上不用move也可以。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值